public bool OrderAttendedTo(float timeSinceLastCheck = 0f) { if (!HumanAIController.IsActive(OrderedCharacter)) { return(false); } // accept only the highest priority order if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder) { #if DEBUG ShipCommandManager.ShipCommandLog($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}"); #endif return(false); } if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter)) { #if DEBUG ShipCommandManager.ShipCommandLog(OrderedCharacter + " was unable to perform assigned order in " + this); #endif return(false); } return(true); }
public bool CreateCombatBehavior(MentalType mentalType) { Character mentalAttackTarget = Character.CharacterList.Where( possibleTarget => HumanAIController.IsActive(possibleTarget) && (possibleTarget.TeamID != character.TeamID || mentalType == MentalType.Berserk) && humanAIController.VisibleHulls.Contains(possibleTarget.CurrentHull) && possibleTarget != character).GetRandom(); if (mentalAttackTarget == null) { return(false); } var combatMode = AIObjectiveCombat.CombatMode.None; bool holdFire = mentalType == MentalType.Afraid && character.IsSecurity; switch (mentalType) { case MentalType.Afraid: combatMode = character.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat; break; case MentalType.Desperate: // might be unnecessary to explicitly declare as arrest against non-humans combatMode = character.IsSecurity && mentalAttackTarget.IsHuman ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Defensive; break; case MentalType.Berserk: combatMode = AIObjectiveCombat.CombatMode.Offensive; break; } // using this as an explicit time-out for the behavior. it's possible it will never run out because of the manager being disabled, but combat objective has failsafes for that mentalBehaviorTimer = MentalBehaviorInterval; humanAIController.AddCombatObjective(combatMode, mentalAttackTarget, allowHoldFire: holdFire, abortCondition: obj => mentalBehaviorTimer <= 0f); string textIdentifier = $"dialogmentalstatereaction{combatMode.ToString().ToLowerInvariant()}"; character.Speak(TextManager.Get(textIdentifier), delay: Rand.Range(0.5f, 1.0f), identifier: textIdentifier, minDurationBetweenSimilar: 25f); if (mentalType == MentalType.Berserk && !character.HasTeamChange(MentalTeamChange)) { // TODO: could this be handled in the switch block above? character.TryAddNewTeamChange(MentalTeamChange, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Absolute, aggressiveBehavior: true)); } return(true); }
public override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return(Priority); } bool isOrder = objectiveManager.HasOrder <AIObjectiveExtinguishFires>(); if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { // Don't go into rooms with any enemies, unless it's an order Priority = 0; Abandon = true; } else { float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull || HumanAIController.VisibleHulls.Contains(targetHull)) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); if (severity > 0.5f && !isOrder) { // Ignore severe fires unless ordered. (Let the fire drain all the oxygen instead). Priority = 0; Abandon = true; } else { float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } } return(Priority); }
protected override bool Check() { if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Abandon = true; return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { Abandon = true; return(false); } bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter); if (isCompleted && targetCharacter != character) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f); } return(isCompleted); }
public override float GetPriority() { if (!objectiveManager.IsCurrentOrder <AIObjectiveExtinguishFires>() && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(0); } float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); float severityFactor = MathHelper.Lerp(0, 1, severity / 100); float devotion = Math.Min(Priority, 10) / 100; return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1))); }
protected override bool Filter(Item item) { if (!IsValidTarget(item, character)) { return(false); } if (item.CurrentHull.FireSources.Count > 0) { return(false); } // Don't repair items in rooms that have enemies inside. if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } if (!Objectives.ContainsKey(item)) { if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { return(false); } } if (RequireAdequateSkills) { if (item.Repairables.Any(r => !r.HasRequiredSkills(character))) { return(false); } } return(true); }
protected override bool Filter(Item target) { // If the target was selected as a valid target, we'll have to accept it so that the objective can be completed. // The validity changes when a character picks the item up. if (!IsValidTarget(target, character, checkInventory: true)) { return(Objectives.ContainsKey(target) && IsItemInsideValidSubmarine(target, character)); } if (target.CurrentHull.FireSources.Count > 0) { return(false); } // Don't repair items in rooms that have enemies inside. if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } return(true); }
public override float GetPriority() { bool isOrder = objectiveManager.CurrentOrder == this; if (!IsAllowed || character.LockHands) { Priority = 0; Abandon = !isOrder; return(Priority); } if (component.Item.ConditionPercentage <= 0) { Priority = 0; } else { if (isOrder) { Priority = AIObjectiveManager.OrderPriority; } ItemComponent target = GetTarget(); Item targetItem = target?.Item; if (targetItem == null) { #if DEBUG DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return(Priority); } var reactor = component?.Item.GetComponent <Reactor>(); if (reactor != null) { if (!isOrder) { if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC || HumanAIController.IsTrueForAnyCrewMember(c => c.ObjectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder.GetTarget() == target)) { Priority = 0; return(Priority); } } switch (Option) { case "shutdown": if (!reactor.PowerOn) { Priority = 0; return(Priority); } break; case "powerup": // Check that we don't already have another order that is targeting the same item. // Without this the autonomous objective will tell the bot to turn the reactor on again. if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target && operateOrder.Option != Option) { Priority = 0; return(Priority); } break; } } if (targetItem.CurrentHull == null || targetItem.Submarine != character.Submarine && !isOrder || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _) || Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)) || component.Item.IgnoreByAI || (useController && controller.Item.IgnoreByAI)) { Priority = 0; } else { float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier); float max = isOrder ? MathHelper.Min(AIObjectiveManager.OrderPriority, 90) : AIObjectiveManager.RunPriority - 1; if (!isOrder && reactor != null && reactor.PowerOn && Option == "powerup") { // Decrease the priority when targeting a reactor that is already on. value /= 2; } Priority = MathHelper.Clamp(value, 0, max); } } return(Priority); }
protected override bool Filter(Item item) { if (!IsValidTarget(item, character)) { return(false); } if (item.CurrentHull.FireSources.Count > 0) { return(false); } // Don't repair items in rooms that have enemies inside. if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } if (!Objectives.ContainsKey(item)) { if (item != character.SelectedConstruction) { float condition = item.ConditionPercentage; if (item.Repairables.All(r => condition >= r.RepairThreshold)) { return(false); } } } if (!string.IsNullOrWhiteSpace(RelevantSkill)) { if (item.Repairables.None(r => r.requiredSkills.Any(s => s.Identifier.Equals(RelevantSkill, StringComparison.OrdinalIgnoreCase)))) { return(false); } } return(true); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (!HumanAIController.IsFriendly(character, target)) { return(false); } if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) > GetVitalityThreshold(humanAI.ObjectiveManager)) { return(false); } } else { if (GetVitalityFactor(target) > vitalityThreshold) { return(false); } } if (target.Submarine == null || character.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.Submarine.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return(false); } return(true); }
void UpdateCommandDecision(float timeSinceLastUpdate) { #if DEBUG ShipCommandLog("Updating command for character " + character); #endif shipGlobalIssues.ForEach(c => c.CalculateGlobalIssue()); AlliedCharacters.Clear(); EnemyCharacters.Clear(); bool isEmergency = false; foreach (Character potentialCharacter in Character.CharacterList) { if (!HumanAIController.IsActive(character)) { continue; } if (HumanAIController.IsFriendly(character, potentialCharacter, true) && potentialCharacter.AIController is HumanAIController) { if (AbleToTakeOrder(potentialCharacter)) { AlliedCharacters.Add(potentialCharacter); } } else { EnemyCharacters.Add(potentialCharacter); if (potentialCharacter.Submarine == CommandedSubmarine) // if enemies are on board, don't issue normal orders anymore { isEmergency = true; } } } attendedIssues.Clear(); availableIssues.Clear(); foreach (ShipIssueWorker shipIssueWorker in ShipIssueWorkers) { float importance = shipIssueWorker.CalculateImportance(isEmergency); if (shipIssueWorker.OrderAttendedTo(timeSinceLastUpdate)) { #if DEBUG ShipCommandLog("Current importance for " + shipIssueWorker + " was " + importance + " and it was already being attended by " + shipIssueWorker.OrderedCharacter); #endif attendedIssues.Add(shipIssueWorker); } else { #if DEBUG ShipCommandLog("Current importance for " + shipIssueWorker + " was " + importance + " and it is not attended to"); #endif shipIssueWorker.RemoveOrder(); availableIssues.Add(shipIssueWorker); } } availableIssues.Sort((x, y) => y.Importance.CompareTo(x.Importance)); attendedIssues.Sort((x, y) => x.Importance.CompareTo(y.Importance)); ShipIssueWorker mostImportantIssue = availableIssues.FirstOrDefault(); float bestValue = 0f; Character bestCharacter = null; if (mostImportantIssue != null && mostImportantIssue.Importance > MinimumIssueThreshold) { IEnumerable <Character> bestCharacters = CrewManager.GetCharactersSortedForOrder(mostImportantIssue.SuggestedOrderPrefab, AlliedCharacters, character, true); foreach (Character orderedCharacter in bestCharacters) { float issueApplicability = mostImportantIssue.Importance; // prefer not to switch if not qualified issueApplicability *= mostImportantIssue.SuggestedOrderPrefab.AppropriateJobs.Contains(orderedCharacter.Info.Job.Prefab.Identifier) ? 1f : 0.75f; ShipIssueWorker occupiedIssue = attendedIssues.FirstOrDefault(i => i.OrderedCharacter == orderedCharacter); if (occupiedIssue != null) { if (occupiedIssue.GetType() == mostImportantIssue.GetType() && mostImportantIssue is ShipIssueWorkerGlobal && occupiedIssue is ShipIssueWorkerGlobal) { continue; } // reverse redundancy to ensure certain issues can be switched over easily (operating weapons) if (mostImportantIssue.AllowEasySwitching && occupiedIssue.AllowEasySwitching) { issueApplicability /= mostImportantIssue.CurrentRedundancy; } // give slight preference if not qualified for current job issueApplicability += occupiedIssue.SuggestedOrderPrefab.AppropriateJobs.Contains(orderedCharacter.Info.Job.Prefab.Identifier) ? 0 : 7.5f; // prefer not to switch orders unless considerably more important issueApplicability -= IssueDevotionBuffer; if (issueApplicability + IssueDevotionBuffer < occupiedIssue.Importance) { continue; } } // prefer first one in bestCharacters in tiebreakers if (issueApplicability > bestValue) { bestValue = issueApplicability; bestCharacter = orderedCharacter; } } } if (bestCharacter != null && mostImportantIssue != null) { #if DEBUG ShipCommandLog("Setting " + mostImportantIssue + " for character " + bestCharacter); #endif mostImportantIssue.SetOrder(bestCharacter); } else // if we didn't give an order, let's try to dismiss someone instead { foreach (ShipIssueWorker shipIssueWorker in ShipIssueWorkers) { if (shipIssueWorker.Importance <= 0f && shipIssueWorker.OrderAttendedTo()) { #if DEBUG ShipCommandLog("Dismissing " + shipIssueWorker + " for character " + shipIssueWorker.OrderedCharacter); #endif Order orderPrefab = Order.GetPrefab("dismissed"); character.Speak(orderPrefab.GetChatMessage(shipIssueWorker.OrderedCharacter.Name, "", givingOrderToSelf: false)); shipIssueWorker.OrderedCharacter.SetOrder(Order.GetPrefab("dismissed"), orderOption: null, priority: 3, character); shipIssueWorker.RemoveOrder(); break; } } } }
public override float GetPriority() { if (!IsAllowed) { Priority = 0; return(Priority); } if (component.Item.ConditionPercentage <= 0) { Priority = 0; } else { if (objectiveManager.CurrentOrder == this) { Priority = AIObjectiveManager.OrderPriority; } Item targetItem = GetTarget()?.Item; if (targetItem == null) { #if DEBUG DebugConsole.ThrowError("Item or component of AI Objective Operate item wass null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return(0.0f); } if (targetItem.CurrentHull == null || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(GetTarget(), out _)) { Priority = 0; } else if (Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { Priority = 0; } else { float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier); float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90); Priority = MathHelper.Clamp(value, 0, max); } } return(Priority); }
protected override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return(Priority); } bool isOrder = objectiveManager.HasOrder <AIObjectiveExtinguishFires>(); if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { // Don't go into rooms with any enemies, unless it's an order Priority = 0; Abandon = true; } else { float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y); yDist = yDist > 100 ? yDist * 3 : 0; float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist)); if (targetHull == character.CurrentHull || HumanAIController.VisibleHulls.Contains(targetHull)) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); if (severity > 0.75f && !isOrder && targetHull.RoomName != null && !targetHull.RoomName.Contains("reactor", StringComparison.OrdinalIgnoreCase) && !targetHull.RoomName.Contains("engine", StringComparison.OrdinalIgnoreCase) && !targetHull.RoomName.Contains("command", StringComparison.OrdinalIgnoreCase)) { // Ignore severe fires to prevent casualities unless ordered to extinguish. Priority = 0; Abandon = true; } else { float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1)); } } return(Priority); }
public override float GetPriority() { if (component.Item.ConditionPercentage <= 0) { return(0); } if (objectiveManager.CurrentOrder == this) { return(AIObjectiveManager.OrderPriority); } if (component.Item.CurrentHull == null) { return(0); } if (component.Item.CurrentHull.FireSources.Count > 0) { return(0); } if (IsOperatedByAnother(GetTarget())) { return(0); } if (Character.CharacterList.Any(c => c.CurrentHull == component.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(0); } float devotion = MathHelper.Min(10, Priority); float value = devotion + AIObjectiveManager.OrderPriority * PriorityModifier; float max = MathHelper.Min((AIObjectiveManager.OrderPriority - 1), 90); return(MathHelper.Clamp(value, 0, max)); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (!HumanAIController.IsFriendly(character, target)) { return(false); } if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return(false); } if (!humanAI.ObjectiveManager.IsCurrentOrder <AIObjectiveRescueAll>()) { // Ignore unsafe hulls, unless ordered if (humanAI.UnsafeHulls.Contains(target.CurrentHull)) { return(false); } } } else { if (GetVitalityFactor(target) >= vitalityThreshold) { return(false); } } if (target.Submarine == null || character.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.Submarine.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null) { if (target.Submarine.Info.Type != character.Submarine.Info.Type) { return(false); } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true)) { return(false); } } if (!target.IsPlayer && HumanAIController.IsActive(target) && target.AIController is HumanAIController targetAI) { // Ignore all concious targets that are currently fighting, fleeing or treating characters if (targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveCombat>() || targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveFindSafety>() || targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveRescue>()) { return(false); } } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return(false); } return(true); }
protected override bool Filter(PowerContainer battery) { if (battery == null) { return(false); } var item = battery.Item; if (item.IgnoreByAI) { return(false); } if (!item.IsInteractable(character)) { return(false); } if (item.Submarine == null) { return(false); } if (item.CurrentHull == null) { return(false); } if (item.Submarine.TeamID != character.TeamID) { return(false); } if (character.Submarine != null) { if (!character.Submarine.IsConnectedTo(item.Submarine)) { return(false); } } if (item.ConditionPercentage <= 0) { return(false); } if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } if (IsReady(battery)) { return(false); } return(true); }
protected override bool Filter(PowerContainer battery) { if (battery == null) { return(false); } var item = battery.Item; if (item.Submarine == null) { return(false); } if (item.CurrentHull == null) { return(false); } if (item.Submarine.TeamID != character.TeamID) { return(false); } if (item.ConditionPercentage <= 0) { return(false); } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { return(false); } if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } if (IsReady(battery)) { return(false); } return(true); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (target.IsInstigator) { return(false); } if (!HumanAIController.IsFriendly(character, target, onlySameTeam: true)) { return(false); } if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) || target.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold)) { return(false); } if (!humanAI.ObjectiveManager.HasOrder <AIObjectiveRescueAll>()) { if (!character.IsMedic && target != character) { // Don't allow to treat others autonomously return(false); } // Ignore unsafe hulls, unless ordered if (humanAI.UnsafeHulls.Contains(target.CurrentHull)) { return(false); } } } else { if (GetVitalityFactor(target) >= vitalityThreshold) { return(false); } } if (target.Submarine == null || character.Submarine == null) { return(false); } // Don't allow going into another sub, unless it's connected and of the same team and type. if (!character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, includingConnectedSubs: true)) { return(false); } if (target != character && !target.IsPlayer && HumanAIController.IsActive(target) && target.AIController is HumanAIController targetAI) { // Ignore all concious targets that are currently fighting, fleeing, fixing, or treating characters if (targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveCombat>() || targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveFindSafety>() || targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveRescue>() || targetAI.ObjectiveManager.HasActiveObjective <AIObjectiveFixLeak>()) { return(false); } } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { return(false); } return(true); }
public override float GetPriority() { if (!IsAllowed) { Priority = 0; return(Priority); } if (component.Item.ConditionPercentage <= 0) { Priority = 0; } else { if (objectiveManager.CurrentOrder == this) { Priority = AIObjectiveManager.OrderPriority; } ItemComponent target = GetTarget(); Item targetItem = target?.Item; if (targetItem == null) { #if DEBUG DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return(Priority); } var reactor = component?.Item.GetComponent <Reactor>(); if (reactor != null) { switch (Option) { case "shutdown": if (!reactor.PowerOn) { Priority = 0; return(Priority); } break; case "powerup": // Check that we don't already have another order that is targeting the same item. // Without this the autonomous objective will tell the bot to turn the reactor on again. if (objectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder != this && operateOrder.GetTarget() == target) { Priority = 0; return(Priority); } break; } } if (targetItem.CurrentHull == null || targetItem.Submarine != character.Submarine && objectiveManager.CurrentOrder != this || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _) || Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { Priority = 0; } else { float value = CumulatedDevotion + (AIObjectiveManager.OrderPriority * PriorityModifier); float max = objectiveManager.CurrentOrder == this ? MathHelper.Min(AIObjectiveManager.OrderPriority, 90) : AIObjectiveManager.RunPriority - 1; Priority = MathHelper.Clamp(value, 0, max); } } return(Priority); }
protected override bool Filter(Pump pump) { if (pump == null) { return(false); } if (pump.Item.NonInteractable) { return(false); } if (pump.Item.HasTag("ballast")) { return(false); } if (pump.Item.Submarine == null) { return(false); } if (pump.Item.CurrentHull == null) { return(false); } if (pump.Item.Submarine.TeamID != character.TeamID) { return(false); } if (pump.Item.ConditionPercentage <= 0) { return(false); } if (pump.Item.CurrentHull.FireSources.Count > 0) { return(false); } if (character.Submarine != null) { if (pump.Item.Submarine.Info.Type != character.Submarine.Info.Type) { return(false); } if (!character.Submarine.IsEntityFoundOnThisSub(pump.Item, true)) { return(false); } } if (Character.CharacterList.Any(c => c.CurrentHull == pump.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return(false); } if (IsReady(pump)) { return(false); } return(true); }
protected override float GetPriority() { bool isOrder = objectiveManager.IsOrder(this); if (!IsAllowed || character.LockHands) { Priority = 0; Abandon = !isOrder; return(Priority); } if (!isOrder && component.Item.ConditionPercentage <= 0) { Priority = 0; } else { if (isOrder) { Priority = objectiveManager.GetOrderPriority(this); } ItemComponent target = GetTarget(); Item targetItem = target?.Item; if (targetItem == null) { #if DEBUG DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen."); #endif Abandon = true; Priority = 0; return(Priority); } var reactor = component?.Item.GetComponent <Reactor>(); if (reactor != null) { if (!isOrder) { if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC) { // The reactor was previously operated by a player -> ignore. Priority = 0; return(Priority); } } switch (Option) { case "shutdown": if (!reactor.PowerOn) { Priority = 0; return(Priority); } break; case "powerup": // Check that we don't already have another order that is targeting the same item. // Without this the autonomous objective will tell the bot to turn the reactor on again. if (IsAnotherOrderTargetingSameItem(objectiveManager.ForcedOrder) || objectiveManager.CurrentOrders.Any(o => IsAnotherOrderTargetingSameItem(o.Objective))) { Priority = 0; return(Priority); } bool IsAnotherOrderTargetingSameItem(AIObjective objective) { return(objective is AIObjectiveOperateItem operateObjective && operateObjective != this && operateObjective.GetTarget() == target && operateObjective.Option != Option); } break; } } else if (!isOrder) { var steering = component?.Item.GetComponent <Steering>(); if (steering != null && (steering.AutoPilot || HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.IsCaptain))) { // Ignore if already set to autopilot or if there's a captain onboard Priority = 0; return(Priority); } } if (targetItem.CurrentHull == null || targetItem.Submarine != character.Submarine && !isOrder || targetItem.CurrentHull.FireSources.Any() || HumanAIController.IsItemOperatedByAnother(target, out _) || Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)) || component.Item.IgnoreByAI(character) || useController && controller.Item.IgnoreByAI(character)) { Priority = 0; } else { if (isOrder) { float max = objectiveManager.GetOrderPriority(this); float value = CumulatedDevotion + (max * PriorityModifier); Priority = MathHelper.Clamp(value, 0, max); } else { float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier); float max = AIObjectiveManager.LowestOrderPriority - 1; if (reactor != null && reactor.PowerOn && reactor.FissionRate > 1 && reactor.AutoTemp && Option == "powerup") { // Already on, no need to operate. value = 0; } Priority = MathHelper.Clamp(value, 0, max); } } } return(Priority); }
protected override bool CheckObjectiveSpecific() { if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { Abandon = true; return(false); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c))) { Abandon = true; return(false); } bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) || targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold); if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f); } return(isCompleted); }
protected override void Act(float deltaTime) { if (character.LockHands) { Abandon = true; return; } ItemComponent target = useController ? controller : component; if (useController && controller == null) { character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name, true), null, 2.0f, "cantfindcontroller", 30.0f); Abandon = true; return; } if (target.CanBeSelected) { if (character.CanInteractWith(target.Item, out _, checkLinked: false)) { HumanAIController.FaceTarget(target.Item); // Don't allow to operate an item that someone already operates, unless this objective is an order if (objectiveManager.CurrentOrder != this && Character.CharacterList.Any(c => c.SelectedConstruction == target.Item && c != character && HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { // Don't abandon return; } if (character.SelectedConstruction != target.Item) { target.Item.TryInteract(character, false, true); } if (component.AIOperate(deltaTime, character, this)) { IsCompleted = completionCondition == null || completionCondition(); } } else { TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager, closeEnough: 50), onAbandon: () => Abandon = true, onCompleted: () => RemoveSubObjective(ref goToObjective)); } } else { if (component.Item.GetComponent <Pickable>() == null) { //controller/target can't be selected and the item cannot be picked -> objective can't be completed Abandon = true; return; } else if (!character.Inventory.Items.Contains(component.Item)) { TryAddSubObjective(ref getItemObjective, () => new AIObjectiveGetItem(character, component.Item, objectiveManager, equip: true), onAbandon: () => Abandon = true, onCompleted: () => RemoveSubObjective(ref getItemObjective)); } else { if (requireEquip && !character.HasEquippedItem(component.Item)) { //the item has to be equipped before using it if it's holdable var holdable = component.Item.GetComponent <Holdable>(); if (holdable == null) { #if DEBUG DebugConsole.ThrowError($"{character.Name}: AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component"); #endif return; } for (int i = 0; i < character.Inventory.Capacity; i++) { if (character.Inventory.SlotTypes[i] == InvSlotType.Any || !holdable.AllowedSlots.Any(s => s.HasFlag(character.Inventory.SlotTypes[i]))) { continue; } //equip slot already taken if (character.Inventory.Items[i] != null) { //try to put the item in an Any slot, and drop it if that fails if (!character.Inventory.Items[i].AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List <InvSlotType>() { InvSlotType.Any })) { character.Inventory.Items[i].Drop(character); } } if (character.Inventory.TryPutItem(component.Item, i, true, false, character)) { component.Item.Equip(character); break; } } return; } if (component.AIOperate(deltaTime, character, this)) { IsCompleted = completionCondition == null || completionCondition(); } } } }