public override float GetPriority() { if (!IsAllowed) { Priority = 0; return(Priority); } if (!objectiveManager.IsCurrentOrder <AIObjectiveExtinguishFires>() && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { Priority = 0; } 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) { distanceFactor = 1; } float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull); float severityFactor = MathHelper.Lerp(0, 1, severity / 100); float devotion = CumulatedDevotion / 100; Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1)); } return(Priority); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.IsDead || target.Removed) { return(false); } if (target == character) { return(false); } if (HumanAIController.IsFriendly(character, target)) { return(false); } if (target.Submarine == null) { return(false); } if (target.Submarine.TeamID != character.TeamID) { return(false); } if (target.CurrentHull == null) { return(false); } if (character.Submarine != null) { if (!character.Submarine.IsConnectedTo(target.Submarine)) { return(false); } } return(true); }
private bool IsOperatedByAnother(ItemComponent target) { foreach (var c in Character.CharacterList) { if (c == character) { continue; } if (!HumanAIController.IsFriendly(c)) { continue; } if (c.SelectedConstruction != target.Item) { continue; } // If the other character is player, don't try to operate if (c.IsRemotePlayer || Character.Controlled == c) { return(true); } if (c.AIController is HumanAIController humanAi) { // If the other character is ordered to operate the item, let him do it if (humanAi.ObjectiveManager.IsCurrentOrder <AIObjectiveOperateItem>()) { return(true); } else { if (target is Steering) { // Steering is hard-coded -> cannot use the required skills collection defined in the xml return(character.GetSkillLevel("helm") <= c.GetSkillLevel("helm")); } else { return(target.DegreeOfSuccess(character) <= target.DegreeOfSuccess(c)); } } } else { // Shouldn't go here, unless we allow non-humans to operate items return(false); } } return(false); }
public override float GetPriority() { if (Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c))) { return(0); } // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist)); 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))); }
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); }
public static bool IsValidTarget(Character target, Character character) { if (target == null || target.Removed) { return(false); } if (target.IsDead) { return(false); } if (target.IsUnconscious && target.Params.Health.ConstantHealthRegeneration <= 0.0f) { return(false); } if (target == character) { return(false); } if (target.Submarine == null) { return(false); } if (character.Submarine == null) { return(false); } if (target.CurrentHull == null) { return(false); } if (HumanAIController.IsFriendly(character, target)) { return(false); } if (!character.Submarine.IsConnectedTo(target.Submarine)) { return(false); } if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { 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 (target.Bleeding < 1 && target.Vitality / target.MaxVitality > GetVitalityThreshold(humanAI.ObjectiveManager)) { return(false); } } else { if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > vitalityThreshold) { return(false); } } if (target.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); } 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; } 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 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); }
protected override bool Filter(Pump pump) { if (pump == null) { 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 && !character.Submarine.IsEntityFoundOnThisSub(pump.Item, true)) { return(false); } if (Character.CharacterList.Any(c => c.CurrentHull == pump.Item.CurrentHull && !HumanAIController.IsFriendly(c))) { return(false); } if (Option == "stoppumping") { if (!pump.IsActive || MathUtils.NearlyEqual(pump.FlowPercentage, 0)) { return(false); } } else { if (!pump.Item.InWater) { return(false); } if (pump.IsActive && pump.FlowPercentage <= -99.9f) { 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); }
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); }
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)); }
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); }
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 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); }
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 void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull); bool needsDivingSuit = needsDivingGear && (currentHull == null || currentHull.WaterPercentage > 90); bool needsEquipment = false; if (needsDivingSuit) { needsEquipment = !HumanAIController.HasDivingSuit(character); } else if (needsDivingGear) { needsEquipment = !HumanAIController.HasDivingMask(character); } if (needsEquipment) { TryAddSubObjective(ref divingGearObjective, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager), onAbandon: () => searchHullTimer = Math.Min(1, searchHullTimer)); } else { if (divingGearObjective != null && divingGearObjective.IsCompleted()) { // Reset the devotion. Priority = 0; divingGearObjective = null; } if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD) { searchHullTimer = Math.Min(1, searchHullTimer); } if (searchHullTimer > 0.0f) { searchHullTimer -= deltaTime; } else { searchHullTimer = SearchHullInterval; previousSafeHull = currentSafeHull; currentSafeHull = FindBestHull(); if (currentSafeHull == null) { currentSafeHull = previousSafeHull; } if (currentSafeHull != null && currentSafeHull != currentHull) { if (goToObjective?.Target != currentSafeHull) { goToObjective = null; } TryAddSubObjective(ref goToObjective, constructor: () => new AIObjectiveGoTo(currentSafeHull, character, objectiveManager, getDivingGearIfNeeded: true) { AllowGoingOutside = HumanAIController.HasDivingSuit(character) }, onAbandon: () => unreachable.Add(goToObjective.Target as Hull)); } else { goToObjective = null; } } if (goToObjective != null) { if (goToObjective.IsCompleted()) { objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } Priority = 0; return; } if (currentHull == null) { return; } //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found) // -> attempt to manually steer away from hazards Vector2 escapeVel = Vector2.Zero; // TODO: optimize foreach (FireSource fireSource in HumanAIController.VisibleHulls.SelectMany(h => h.FireSources)) { Vector2 dir = character.Position - fireSource.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } foreach (Character enemy in Character.CharacterList) { if (enemy.IsDead || enemy.IsUnconscious || enemy.Removed || HumanAIController.IsFriendly(enemy)) { continue; } if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull)) { Vector2 dir = character.Position - enemy.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } } if (escapeVel != Vector2.Zero) { float left = currentHull.Rect.X + 50; float right = currentHull.Rect.Right - 50; //only move if we haven't reached the edge of the room if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right) { character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel); } else { character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left; character.AIController.SteeringManager.Reset(); } } else { Priority = 0; objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } } }
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); }
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(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))) { return(false); } if (Option == "charge") { if (battery.RechargeRatio >= PowerContainer.aiRechargeTargetRatio - 0.01f) { return(false); } } else { if (battery.RechargeRatio <= 0) { 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); }
protected override void Act(float deltaTime) { 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)) { // 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))) { abandon = true; return; } if (character.SelectedConstruction != target.Item) { target.Item.TryInteract(character, false, true); } if (component.AIOperate(deltaTime, character, this)) { isCompleted = true; } } else { TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager)); } } 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)); } 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("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 = true; } } } }
private void Attack(float deltaTime) { character.CursorPosition = Enemy.Position; if (!character.CanSeeCharacter(Enemy)) { return; } if (Weapon.RequireAimToUse) { bool isOperatingButtons = false; if (SteeringManager == PathSteering) { var door = PathSteering.CurrentPath?.CurrentNode?.ConnectedDoor; if (door != null && !door.IsOpen && !door.IsBroken) { isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents <Controller>(true).Any(); } } if (!isOperatingButtons) { character.SetInput(InputType.Aim, false, true); } } bool isFacing = character.AnimController.Dir > 0 && Enemy.WorldPosition.X > character.WorldPosition.X || character.AnimController.Dir < 0 && Enemy.WorldPosition.X < character.WorldPosition.X; if (!isFacing) { aimTimer = Rand.Range(1f, 1.5f); } if (aimTimer > 0) { aimTimer -= deltaTime; return; } if (WeaponComponent is MeleeWeapon meleeWeapon) { if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range) { character.SetInput(InputType.Shoot, false, true); Weapon.Use(deltaTime, character); } } else { if (WeaponComponent is RepairTool repairTool) { if (Vector2.DistanceSquared(character.Position, Enemy.Position) > repairTool.Range * repairTool.Range) { return; } } if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4) { if (myBodies == null) { myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody); } var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel; var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories); if (pickedBody != null) { Character target = null; if (pickedBody.UserData is Character c) { target = c; } else if (pickedBody.UserData is Limb limb) { target = limb.character; } if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target))) { character.SetInput(InputType.Shoot, false, true); Weapon.Use(deltaTime, character); float reloadTime = 0; if (WeaponComponent is RangedWeapon rangedWeapon) { reloadTime = rangedWeapon.Reload; } if (WeaponComponent is MeleeWeapon mw) { reloadTime = mw.Reload; } aimTimer = reloadTime * Rand.Range(1f, 1.5f); } } } } }
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); }
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); }
public override float GetPriority() { if (targetCharacter == null) { return(0); } if (targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead) { abandon = true; return(0); } // Don't go into rooms that have enemies if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(c))) { abandon = true; return(0); } // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - targetCharacter.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetCharacter.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.5f, MathUtils.InverseLerp(0, 10000, dist)); float vitalityFactor = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter); float devotion = Math.Min(Priority, 10) / 100; return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + vitalityFactor * distanceFactor, 0, 1))); }
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 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); }