Merge branch 'dev' of https://github.com/Regalis11/Barotrauma.git into unstable-tests
This commit is contained in:
@@ -10,8 +10,8 @@ namespace Barotrauma
|
||||
{
|
||||
public virtual float Devotion => AIObjectiveManager.baseDevotion;
|
||||
|
||||
public abstract string Identifier { get; set; }
|
||||
public virtual string DebugTag => Identifier;
|
||||
public abstract Identifier Identifier { get; set; }
|
||||
public virtual string DebugTag => Identifier.Value;
|
||||
public virtual bool ForceRun => false;
|
||||
public virtual bool IgnoreUnsafeHulls => false;
|
||||
public virtual bool AbandonWhenCannotCompleteSubjectives => true;
|
||||
@@ -83,7 +83,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly Character character;
|
||||
public readonly AIObjectiveManager objectiveManager;
|
||||
public string Option { get; private set; }
|
||||
public readonly Identifier Option;
|
||||
|
||||
private bool _abandon;
|
||||
public bool Abandon
|
||||
@@ -157,11 +157,11 @@ namespace Barotrauma
|
||||
return subObjective == null ? this : subObjective.GetActiveObjective();
|
||||
}
|
||||
|
||||
public AIObjective(Character character, AIObjectiveManager objectiveManager, float priorityModifier, string option = null)
|
||||
public AIObjective(Character character, AIObjectiveManager objectiveManager, float priorityModifier, Identifier option = default)
|
||||
{
|
||||
this.objectiveManager = objectiveManager;
|
||||
this.character = character;
|
||||
Option = option ?? string.Empty;
|
||||
Option = option;
|
||||
PriorityModifier = priorityModifier;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -9,11 +9,11 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveChargeBatteries : AIObjectiveLoop<PowerContainer>
|
||||
{
|
||||
public override string Identifier { get; set; } = "charge batteries";
|
||||
public override Identifier Identifier { get; set; } = "charge batteries".ToIdentifier();
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
private IEnumerable<PowerContainer> batteryList;
|
||||
|
||||
public AIObjectiveChargeBatteries(Character character, AIObjectiveManager objectiveManager, string option, float priorityModifier)
|
||||
public AIObjectiveChargeBatteries(Character character, AIObjectiveManager objectiveManager, Identifier option, float priorityModifier)
|
||||
: base(character, objectiveManager, priorityModifier, option) { }
|
||||
|
||||
protected override bool Filter(PowerContainer battery)
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
}
|
||||
if (item.ConditionPercentage <= 0) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { 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;
|
||||
@@ -55,7 +56,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character == null || character.Submarine == null)
|
||||
{
|
||||
return new PowerContainer[0];
|
||||
return Array.Empty<PowerContainer>();
|
||||
}
|
||||
batteryList = character.Submarine.GetItems(true).Select(i => i.GetComponent<PowerContainer>()).Where(b => b != null);
|
||||
}
|
||||
|
||||
+19
-17
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCleanupItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "cleanup item";
|
||||
public override Identifier Identifier { get; set; } = "cleanup item".ToIdentifier();
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowAutomaticItemUnequipping => false;
|
||||
|
||||
@@ -61,21 +61,6 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (item.IgnoreByAI(character))
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (item.ParentInventory != null)
|
||||
{
|
||||
if (item.Container != null && !AIObjectiveCleanupItems.IsValidContainer(item.Container, character, allowUnloading: objectiveManager.HasOrder<AIObjectiveCleanupItems>()))
|
||||
{
|
||||
// Target was picked up or moved by someone.
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Only continue when the get item sub objectives have been completed.
|
||||
if (subObjectives.Any()) { return; }
|
||||
if (HumanAIController.FindSuitableContainer(character, item, ignoredContainers, ref itemIndex, out Item suitableContainer))
|
||||
{
|
||||
@@ -133,7 +118,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific() => IsCompleted;
|
||||
protected override bool CheckObjectiveSpecific()
|
||||
{
|
||||
if (item.IgnoreByAI(character))
|
||||
{
|
||||
Abandon = true;
|
||||
return false;
|
||||
}
|
||||
if (item.ParentInventory != null)
|
||||
{
|
||||
if (item.Container != null && !AIObjectiveCleanupItems.IsValidContainer(item.Container, character, allowUnloading: objectiveManager.HasOrder<AIObjectiveCleanupItems>()))
|
||||
{
|
||||
// Target was picked up or moved by someone.
|
||||
Abandon = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return IsCompleted;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
|
||||
+7
-7
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCleanupItems : AIObjectiveLoop<Item>
|
||||
{
|
||||
public override string Identifier { get; set; } = "cleanup items";
|
||||
public override Identifier Identifier { get; set; } = "cleanup items".ToIdentifier();
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowAutomaticItemUnequipping => false;
|
||||
protected override bool ForceOrderPriority => false;
|
||||
@@ -79,18 +79,17 @@ namespace Barotrauma
|
||||
|
||||
public static bool IsValidContainer(Item container, Character character, bool allowUnloading = true) =>
|
||||
allowUnloading &&
|
||||
!container.IgnoreByAI(character) &&
|
||||
container.IsInteractable(character) &&
|
||||
container.HasAccess(character) &&
|
||||
container.HasTag("allowcleanup") &&
|
||||
container.ParentInventory == null && container.OwnInventory != null && container.OwnInventory.AllItems.Any() &&
|
||||
container.GetComponent<ItemContainer>() is ItemContainer itemContainer && itemContainer.HasAccess(character) &&
|
||||
IsItemInsideValidSubmarine(container, character);
|
||||
container.GetComponent<ItemContainer>() != null &&
|
||||
IsItemInsideValidSubmarine(container, character) &&
|
||||
!container.IsClaimedByBallastFlora;
|
||||
|
||||
public static bool IsValidTarget(Item item, Character character, bool checkInventory, bool allowUnloading = true)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (item.IgnoreByAI(character)) { return false; }
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
if ((item.SpawnedInCurrentOutpost && !item.AllowStealing) == character.IsOnPlayerTeam) { return false; }
|
||||
if (item.ParentInventory != null)
|
||||
{
|
||||
@@ -102,6 +101,7 @@ namespace Barotrauma
|
||||
if (!IsValidContainer(item.Container, character, allowUnloading)) { return false; }
|
||||
}
|
||||
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||
if (item.HasBallastFloraInHull) { return false; }
|
||||
var pickable = item.GetComponent<Pickable>();
|
||||
if (pickable == null) { return false; }
|
||||
if (pickable is Holdable h && h.Attachable && h.Attached) { return false; }
|
||||
|
||||
+27
-23
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCombat : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "combat";
|
||||
public override Identifier Identifier { get; set; } = "combat".ToIdentifier();
|
||||
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
@@ -212,10 +212,14 @@ namespace Barotrauma
|
||||
|
||||
protected override bool CheckObjectiveSpecific()
|
||||
{
|
||||
if (sqrDistance > maxDistance * maxDistance)
|
||||
if (character.Submarine == null || character.Submarine.TeamID != CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
// The target escaped from us.
|
||||
return true;
|
||||
// Can't lose the target in friendly outposts.
|
||||
if (sqrDistance > maxDistance * maxDistance)
|
||||
{
|
||||
// The target escaped from us.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return IsEnemyDisabled || (AllowCoolDown && coolDownTimer <= 0);
|
||||
}
|
||||
@@ -250,17 +254,17 @@ namespace Barotrauma
|
||||
case CombatMode.Offensive:
|
||||
if (TargetEliminated && objectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>())
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogTargetDown"), null, 3.0f, "targetdown", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogTargetDown").Value, null, 3.0f, "targetdown".ToIdentifier(), 30.0f);
|
||||
}
|
||||
break;
|
||||
case CombatMode.Arrest:
|
||||
if (HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true))
|
||||
if (HumanAIController.HasItem(Enemy, "handlocker".ToIdentifier(), out _, requireEquipped: true))
|
||||
{
|
||||
IsCompleted = true;
|
||||
}
|
||||
else if (Enemy.IsKnockedDown &&
|
||||
!objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>() &&
|
||||
!HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false))
|
||||
!HumanAIController.HasItem(character, "handlocker".ToIdentifier(), out _, requireEquipped: false))
|
||||
{
|
||||
IsCompleted = true;
|
||||
}
|
||||
@@ -399,7 +403,7 @@ namespace Barotrauma
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
RemoveSubObjective(ref followTargetObjective);
|
||||
TryAddSubObjective(ref seekWeaponObjective,
|
||||
constructor: () => new AIObjectiveGetItem(character, "weapon", objectiveManager, equip: true, checkInventory: false)
|
||||
constructor: () => new AIObjectiveGetItem(character, "weapon".ToIdentifier(), objectiveManager, equip: true, checkInventory: false)
|
||||
{
|
||||
AllowStealing = HumanAIController.IsMentallyUnstable,
|
||||
EvaluateCombatPriority = false, // Use a custom formula instead
|
||||
@@ -636,7 +640,7 @@ namespace Barotrauma
|
||||
// If there's an item container that takes a battery,
|
||||
// assume that it's required for the stun effect
|
||||
// as we can't check the status effect conditions here.
|
||||
var mobileBatteryTag = "mobilebattery";
|
||||
var mobileBatteryTag = "mobilebattery".ToIdentifier();
|
||||
var containers = weapon.Item.Components.Where(ic =>
|
||||
ic is ItemContainer container &&
|
||||
container.ContainableItemIdentifiers.Contains(mobileBatteryTag));
|
||||
@@ -777,7 +781,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (retreatTarget != null && character.CurrentHull != retreatTarget)
|
||||
{
|
||||
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)
|
||||
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager)
|
||||
{
|
||||
UsePathingOutside = false
|
||||
},
|
||||
@@ -848,7 +852,7 @@ namespace Barotrauma
|
||||
if (followTargetObjective == null) { return; }
|
||||
if (Mode == CombatMode.Arrest && Enemy.IsKnockedDown)
|
||||
{
|
||||
if (HumanAIController.HasItem(character, "handlocker", out _))
|
||||
if (HumanAIController.HasItem(character, "handlocker".ToIdentifier(), out _))
|
||||
{
|
||||
if (!arrestingRegistered)
|
||||
{
|
||||
@@ -861,10 +865,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
ItemPrefab prefab = ItemPrefab.Find(null, "handcuffs");
|
||||
ItemPrefab prefab = ItemPrefab.Find(null, "handcuffs".ToIdentifier());
|
||||
if (prefab != null)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(prefab, character.Inventory, onSpawned: (Item i) => i.SpawnedInCurrentOutpost = true);
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, character.Inventory, onSpawned: (Item i) => i.SpawnedInCurrentOutpost = true);
|
||||
}
|
||||
}
|
||||
RemoveFollowTarget();
|
||||
@@ -914,7 +918,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
|
||||
if (HumanAIController.HasItem(character, "handlocker".ToIdentifier(), out IEnumerable<Item> matchingItems) && !Enemy.IsUnconscious && Enemy.IsKnockedDown && character.CanInteractWith(Enemy))
|
||||
{
|
||||
var handCuffs = matchingItems.First();
|
||||
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true))
|
||||
@@ -928,7 +932,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogTargetArrested").Value, null, 3.0f, "targetarrested".ToIdentifier(), 30.0f);
|
||||
}
|
||||
if (!objectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>())
|
||||
{
|
||||
@@ -939,7 +943,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Seeks for more ammunition. Creates a new subobjective.
|
||||
/// </summary>
|
||||
private void SeekAmmunition(string[] ammunitionIdentifiers)
|
||||
private void SeekAmmunition(Identifier[] ammunitionIdentifiers)
|
||||
{
|
||||
retreatTarget = null;
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
@@ -974,7 +978,7 @@ namespace Barotrauma
|
||||
HumanAIController.UnequipEmptyItems(Weapon);
|
||||
RelatedItem item = null;
|
||||
Item ammunition = null;
|
||||
string[] ammunitionIdentifiers = null;
|
||||
Identifier[] ammunitionIdentifiers = null;
|
||||
if (WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
|
||||
{
|
||||
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
@@ -1212,17 +1216,17 @@ namespace Barotrauma
|
||||
retreatTarget = null;
|
||||
}
|
||||
|
||||
private void SpeakNoWeapons() => Speak("dialogcombatnoweapons", delay: 0, minDuration: 30);
|
||||
private void AskHelp() => Speak("dialogcombatretreating", delay: Rand.Range(0f, 1f), minDuration: 20);
|
||||
private void SpeakNoWeapons() => Speak("dialogcombatnoweapons".ToIdentifier(), delay: 0, minDuration: 30);
|
||||
private void AskHelp() => Speak("dialogcombatretreating".ToIdentifier(), delay: Rand.Range(0f, 1f), minDuration: 20);
|
||||
|
||||
private void Speak(string textIdentifier, float delay, float minDuration)
|
||||
private void Speak(Identifier textIdentifier, float delay, float minDuration)
|
||||
{
|
||||
if (character.IsOnPlayerTeam && !character.IsInFriendlySub)
|
||||
{
|
||||
string msg = TextManager.Get(textIdentifier, true);
|
||||
if (msg != null)
|
||||
LocalizedString msg = TextManager.Get(textIdentifier);
|
||||
if (!msg.IsNullOrEmpty())
|
||||
{
|
||||
character.Speak(msg, identifier: textIdentifier, delay: delay, minDurationBetweenSimilar: minDuration);
|
||||
character.Speak(msg.Value, identifier: textIdentifier, delay: delay, minDurationBetweenSimilar: minDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-24
@@ -7,18 +7,18 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveContainItem: AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "contain item";
|
||||
public override Identifier Identifier { get; set; } = "contain item".ToIdentifier();
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
public Identifier[] ignoredContainerIdentifiers;
|
||||
public bool checkInventory = true;
|
||||
|
||||
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs and in some cases also enemy NPCs, like pirates)
|
||||
private readonly bool spawnItemIfNotFound;
|
||||
|
||||
//can either be a tag or an identifier
|
||||
public readonly string[] itemIdentifiers;
|
||||
public readonly Identifier[] itemIdentifiers;
|
||||
public readonly ItemContainer container;
|
||||
private readonly Item item;
|
||||
public Item ItemToContain { get; private set; }
|
||||
@@ -60,25 +60,21 @@ namespace Barotrauma
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public AIObjectiveContainItem(Character character, string itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: this(character, new string[] { itemIdentifier }, container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
|
||||
public AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: this(character, new Identifier[] { itemIdentifier }, container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
|
||||
|
||||
public AIObjectiveContainItem(Character character, string[] itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
public AIObjectiveContainItem(Character character, Identifier[] itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.itemIdentifiers = itemIdentifiers;
|
||||
this.spawnItemIfNotFound = spawnItemIfNotFound;
|
||||
for (int i = 0; i < itemIdentifiers.Length; i++)
|
||||
{
|
||||
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
||||
}
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific()
|
||||
{
|
||||
if (IsCompleted) { return true; }
|
||||
if (container == null || (container.Item != null && container.Item.IsThisOrAnyContainerIgnoredByAI(character)))
|
||||
if (container?.Item == null || !container.Item.HasAccess(character))
|
||||
{
|
||||
Abandon = true;
|
||||
return false;
|
||||
@@ -89,23 +85,28 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
int containedItemCount = 0;
|
||||
foreach (Item it in container.Inventory.AllItems)
|
||||
{
|
||||
if (CheckItem(it))
|
||||
{
|
||||
containedItemCount++;
|
||||
}
|
||||
}
|
||||
return containedItemCount >= ItemCount;
|
||||
return CountItems();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage >= ConditionLevel && !i.IsThisOrAnyContainerIgnoredByAI(character);
|
||||
private bool CountItems()
|
||||
{
|
||||
int containedItemCount = 0;
|
||||
foreach (Item it in container.Inventory.AllItems)
|
||||
{
|
||||
if (CheckItem(it))
|
||||
{
|
||||
containedItemCount++;
|
||||
}
|
||||
}
|
||||
return containedItemCount >= ItemCount;
|
||||
}
|
||||
|
||||
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage >= ConditionLevel && i.HasAccess(character);
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character))
|
||||
if (container?.Item == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
@@ -141,8 +142,8 @@ namespace Barotrauma
|
||||
container.Inventory.TryPutItem(item, null);
|
||||
}
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
IsCompleted = item != null || CountItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -159,7 +160,7 @@ namespace Barotrauma
|
||||
{
|
||||
TargetName = container.Item.Name,
|
||||
AbortCondition = obj =>
|
||||
container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character) ||
|
||||
container?.Item == null || container.Item.Removed || !container.Item.HasAccess(character) ||
|
||||
(container.Item.GetRootContainer()?.OwnInventory?.Locked ?? false) ||
|
||||
ItemToContain == null || ItemToContain.Removed ||
|
||||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveDecontainItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "decontain item";
|
||||
public override Identifier Identifier { get; set; } = "decontain item".ToIdentifier();
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Barotrauma
|
||||
RemoveExistingPredicate = RemoveExistingPredicate,
|
||||
RemoveMax = RemoveExistingMax,
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = sourceContainer != null ? new string[] { sourceContainer.Item.Prefab.Identifier } : null
|
||||
ignoredContainerIdentifiers = sourceContainer != null ? new Identifier[] { sourceContainer.Item.Prefab.Identifier } : null
|
||||
},
|
||||
onCompleted: () => IsCompleted = true,
|
||||
onAbandon: () => Abandon = true);
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
class AIObjectiveEscapeHandcuffs : AIObjective
|
||||
{
|
||||
// Used for prisoner escorts to allow them to escape their binds
|
||||
public override string Identifier { get; set; } = "escape handcuffs";
|
||||
public override Identifier Identifier { get; set; } = "escape handcuffs".ToIdentifier();
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma
|
||||
escapeProgress += Rand.Range(2, 5);
|
||||
if (escapeProgress > 15)
|
||||
{
|
||||
Item handcuffs = character.Inventory.FindItemByTag("handlocker");
|
||||
Item handcuffs = character.Inventory.FindItemByTag("handlocker".ToIdentifier());
|
||||
if (handcuffs != null)
|
||||
{
|
||||
handcuffs.Drop(character);
|
||||
|
||||
+16
-9
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveExtinguishFire : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "extinguish fire";
|
||||
public override Identifier Identifier { get; set; } = "extinguish fire".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool ConcurrentObjectives => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
@@ -77,16 +77,16 @@ namespace Barotrauma
|
||||
private float sinTime;
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
var extinguisherItem = character.Inventory.FindItemByTag("fireextinguisher");
|
||||
var extinguisherItem = character.Inventory.FindItemByTag("fireextinguisher".ToIdentifier());
|
||||
if (extinguisherItem == null || extinguisherItem.Condition <= 0.0f || !character.HasEquippedItem(extinguisherItem))
|
||||
{
|
||||
TryAddSubObjective(ref getExtinguisherObjective, () =>
|
||||
{
|
||||
if (character.IsOnPlayerTeam && !character.HasEquippedItem("fireextinguisher", allowBroken: false))
|
||||
if (character.IsOnPlayerTeam && !character.HasEquippedItem("fireextinguisher".ToIdentifier(), allowBroken: false))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogFindExtinguisher"), null, 2.0f, "findextinguisher", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogFindExtinguisher").Value, null, 2.0f, "findextinguisher".ToIdentifier(), 30.0f);
|
||||
}
|
||||
var getItemObjective = new AIObjectiveGetItem(character, "fireextinguisher", objectiveManager, equip: true)
|
||||
var getItemObjective = new AIObjectiveGetItem(character, "fireextinguisher".ToIdentifier(), objectiveManager, equip: true)
|
||||
{
|
||||
AllowStealing = true,
|
||||
// If the item is inside an unsafe hull, decrease the priority
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
};
|
||||
if (objectiveManager.HasOrder<AIObjectiveExtinguishFires>())
|
||||
{
|
||||
getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindfireextinguisher"), null, 0.0f, "dialogcannotfindfireextinguisher", 10.0f);
|
||||
getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindfireextinguisher").Value, null, 0.0f, "dialogcannotfindfireextinguisher".ToIdentifier(), 10.0f);
|
||||
};
|
||||
return getItemObjective;
|
||||
});
|
||||
@@ -112,6 +112,13 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (FireSource fs in targetHull.FireSources)
|
||||
{
|
||||
if (fs == null) { continue; }
|
||||
if (fs.Removed) { continue; }
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
Abandon = true;
|
||||
break;
|
||||
}
|
||||
float xDist = Math.Abs(character.WorldPosition.X - fs.WorldPosition.X) - fs.DamageRange;
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - fs.WorldPosition.Y);
|
||||
bool inRange = xDist + yDist < extinguisher.Range;
|
||||
@@ -139,7 +146,7 @@ namespace Barotrauma
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.DisplayName, true), null, 0, "putoutfire", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariable("DialogPutOutFire", "[roomname]", targetHull.DisplayName, FormatCapitals.Yes).Value, null, 0, "putoutfire".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
if (move)
|
||||
@@ -147,13 +154,13 @@ namespace Barotrauma
|
||||
//go to the first firesource
|
||||
if (TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: Math.Max(fs.DamageRange, extinguisher.Range * 0.7f))
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire",
|
||||
DialogueIdentifier = "dialogcannotreachfire".ToIdentifier(),
|
||||
TargetName = fs.Hull.DisplayName
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective)))
|
||||
{
|
||||
gotoObjective.requiredCondition = () => targetHull == null || character.CanSeeTarget(targetHull);
|
||||
gotoObjective.requiredCondition = () => character.CanSeeTarget(targetHull);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveExtinguishFires : AIObjectiveLoop<Hull>
|
||||
{
|
||||
public override string Identifier { get; set; } = "extinguish fires";
|
||||
public override Identifier Identifier { get; set; } = "extinguish fires".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public static float GetFireSeverity(Hull hull) => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 500, hull.FireSources.Sum(fs => fs.Size.X)));
|
||||
|
||||
protected override IEnumerable<Hull> GetList() => Hull.hullList;
|
||||
protected override IEnumerable<Hull> GetList() => Hull.HullList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Hull target)
|
||||
=> new AIObjectiveExtinguishFire(character, target, objectiveManager, PriorityModifier);
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFightIntruders : AIObjectiveLoop<Character>
|
||||
{
|
||||
public override string Identifier { get; set; } = "fight intruders";
|
||||
public override Identifier Identifier { get; set; } = "fight intruders".ToIdentifier();
|
||||
protected override float IgnoreListClearInterval => 30;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
|
||||
@@ -45,9 +45,9 @@ namespace Barotrauma
|
||||
{
|
||||
//hold fire while the enemy is in the airlock (except if they've attacked us)
|
||||
if (character.GetDamageDoneByAttacker(target) > 0.0f) { return false; }
|
||||
return target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t.Equals("airlock", System.StringComparison.OrdinalIgnoreCase));
|
||||
return target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t == "airlock");
|
||||
};
|
||||
character.Speak(TextManager.Get("dialogenteroutpostwarning"), null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning", 30.0f);
|
||||
character.Speak(TextManager.Get("dialogenteroutpostwarning").Value, null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning".ToIdentifier(), 30.0f);
|
||||
}
|
||||
}
|
||||
return combatObjective;
|
||||
|
||||
+13
-13
@@ -7,13 +7,13 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFindDivingGear : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "find diving gear";
|
||||
public override Identifier Identifier { get; set; } = "find diving gear".ToIdentifier();
|
||||
public override string DebugTag => $"{Identifier} ({gearTag})";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
|
||||
private readonly string gearTag;
|
||||
private readonly Identifier gearTag;
|
||||
|
||||
private AIObjectiveGetItem getDivingGear;
|
||||
private AIObjectiveContainItem getOxygen;
|
||||
@@ -21,13 +21,13 @@ namespace Barotrauma
|
||||
|
||||
public const float MIN_OXYGEN = 10;
|
||||
|
||||
public const string HEAVY_DIVING_GEAR = "deepdiving";
|
||||
public const string LIGHT_DIVING_GEAR = "lightdiving";
|
||||
public static readonly Identifier HEAVY_DIVING_GEAR = "deepdiving".ToIdentifier();
|
||||
public static readonly Identifier LIGHT_DIVING_GEAR = "lightdiving".ToIdentifier();
|
||||
/// <summary>
|
||||
/// Diving gear that's suitable for wearing indoors (-> the bots don't try to unequip it when they don't need diving gear)
|
||||
/// </summary>
|
||||
public const string DIVING_GEAR_WEARABLE_INDOORS = "divinggear_wearableindoors";
|
||||
public const string OXYGEN_SOURCE = "oxygensource";
|
||||
public static readonly Identifier DIVING_GEAR_WEARABLE_INDOORS = "divinggear_wearableindoors".ToIdentifier();
|
||||
public static readonly Identifier OXYGEN_SOURCE = "oxygensource".ToIdentifier();
|
||||
|
||||
protected override bool CheckObjectiveSpecific() => targetItem != null && character.HasEquippedItem(targetItem, slotType: InvSlotType.OuterClothes | InvSlotType.Head);
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetItem == null && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogGetDivingGear"), null, 0.0f, "getdivinggear", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogGetDivingGear").Value, null, 0.0f, "getdivinggear".ToIdentifier(), 30.0f);
|
||||
}
|
||||
return new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true)
|
||||
{
|
||||
@@ -92,15 +92,15 @@ namespace Barotrauma
|
||||
{
|
||||
if (HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: min))
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogswappingoxygentank"), null, 0, "swappingoxygentank", 30.0f);
|
||||
character.Speak(TextManager.Get("dialogswappingoxygentank").Value, null, 0, "swappingoxygentank".ToIdentifier(), 30.0f);
|
||||
if (character.Inventory.FindAllItems(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > min).Count == 1)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoglastoxygentank"), null, 0.0f, "dialoglastoxygentank", 30.0f);
|
||||
character.Speak(TextManager.Get("dialoglastoxygentank").Value, null, 0.0f, "dialoglastoxygentank".ToIdentifier(), 30.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogGetOxygenTank").Value, null, 0, "getoxygentank".ToIdentifier(), 30.0f);
|
||||
}
|
||||
}
|
||||
return new AIObjectiveContainItem(character, OXYGEN_SOURCE, targetItem.GetComponent<ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
if (remainingTanks > 0 && !HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: 0.01f))
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcantfindtoxygen"), null, 0, "cantfindoxygen", 30.0f);
|
||||
character.Speak(TextManager.Get("dialogcantfindtoxygen").Value, null, 0, "cantfindoxygen".ToIdentifier(), 30.0f);
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref getOxygen));
|
||||
@@ -147,11 +147,11 @@ namespace Barotrauma
|
||||
int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > 1);
|
||||
if (remainingOxygenTanks == 0)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogOutOfOxygenTanks"), null, 0.0f, "outofoxygentanks", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogOutOfOxygenTanks").Value, null, 0.0f, "outofoxygentanks".ToIdentifier(), 30.0f);
|
||||
}
|
||||
else if (remainingOxygenTanks < 10)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLowOnOxygenTanks"), null, 0.0f, "lowonoxygentanks", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogLowOnOxygenTanks").Value, null, 0.0f, "lowonoxygentanks".ToIdentifier(), 30.0f);
|
||||
}
|
||||
return remainingOxygenTanks;
|
||||
}
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFindSafety : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "find safety";
|
||||
public override Identifier Identifier { get; set; } = "find safety".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
@@ -317,7 +317,7 @@ namespace Barotrauma
|
||||
Hull bestHull = null;
|
||||
float bestValue = 0;
|
||||
bool bestIsAirlock = false;
|
||||
foreach (Hull hull in Hull.hullList.OrderByDescending(h => EstimateHullSuitability(h)))
|
||||
foreach (Hull hull in Hull.HullList.OrderByDescending(h => EstimateHullSuitability(h)))
|
||||
{
|
||||
if (hull.Submarine == null) { continue; }
|
||||
// Ruins are mazes filled with water. There's no safe hulls and we don't want to use the resources on it.
|
||||
@@ -342,7 +342,7 @@ namespace Barotrauma
|
||||
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
|
||||
if (hullSafety < bestValue) { continue; }
|
||||
//avoid airlock modules if not allowed to change the sub
|
||||
if (!allowChangingTheSubmarine && hull.OutpostModuleTags.Any(t => t.Equals("airlock", StringComparison.OrdinalIgnoreCase)))
|
||||
if (!allowChangingTheSubmarine && hull.OutpostModuleTags.Any(t => t == "airlock"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
+12
-11
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFixLeak : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "fix leak";
|
||||
public override Identifier Identifier { get; set; } = "fix leak".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
@@ -64,15 +64,15 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
var weldingTool = character.Inventory.FindItemByTag("weldingequipment", true);
|
||||
var weldingTool = character.Inventory.FindItemByTag("weldingequipment".ToIdentifier(), true);
|
||||
if (weldingTool == null)
|
||||
{
|
||||
TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingequipment", objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
|
||||
TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingequipment".ToIdentifier(), objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
|
||||
onAbandon: () =>
|
||||
{
|
||||
if (character.IsOnPlayerTeam && objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>())
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogcannotfindweldingequipment"), null, 0.0f, "dialogcannotfindweldingequipment", 10.0f);
|
||||
character.Speak(TextManager.Get("dialogcannotfindweldingequipment").Value, null, 0.0f, "dialogcannotfindweldingequipment".ToIdentifier(), 10.0f);
|
||||
}
|
||||
Abandon = true;
|
||||
},
|
||||
@@ -91,7 +91,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (weldingTool.OwnInventory != null && weldingTool.OwnInventory.AllItems.None(i => i.HasTag("weldingfuel") && i.Condition > 0.0f))
|
||||
{
|
||||
TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfuel", weldingTool.GetComponent<ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfuel".ToIdentifier(), weldingTool.GetComponent<ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
RemoveExisting = true
|
||||
},
|
||||
@@ -112,11 +112,11 @@ namespace Barotrauma
|
||||
int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("weldingfuel") && i.Condition > 1);
|
||||
if (remainingOxygenTanks == 0)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogOutOfWeldingFuel"), null, 0.0f, "outofweldingfuel", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogOutOfWeldingFuel").Value, null, 0.0f, "outofweldingfuel".ToIdentifier(), 30.0f);
|
||||
}
|
||||
else if (remainingOxygenTanks < 4)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLowOnWeldingFuel"), null, 0.0f, "lowonweldingfuel", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogLowOnWeldingFuel").Value, null, 0.0f, "lowonweldingfuel".ToIdentifier(), 30.0f);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -142,7 +142,7 @@ namespace Barotrauma
|
||||
bool canOperate = toLeak.LengthSquared() < reach * reach;
|
||||
if (canOperate)
|
||||
{
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak),
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: Identifier.Empty, requireEquip: true, operateTarget: Leak),
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () =>
|
||||
{
|
||||
@@ -160,10 +160,11 @@ namespace Barotrauma
|
||||
{
|
||||
UseDistanceRelativeToAimSourcePos = true,
|
||||
CloseEnough = reach,
|
||||
DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak" : null,
|
||||
DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak".ToIdentifier() : Identifier.Empty,
|
||||
TargetName = Leak.FlowTargetHull?.DisplayName,
|
||||
CheckVisibility = false,
|
||||
requiredCondition = () => Leak.Submarine == character.Submarine,
|
||||
requiredCondition = () =>
|
||||
Leak.Submarine == character.Submarine &&
|
||||
Leak.linkedTo.Any(e => e is Hull h && character.CurrentHull == h),
|
||||
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
|
||||
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
|
||||
},
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveFixLeaks : AIObjectiveLoop<Gap>
|
||||
{
|
||||
public override string Identifier { get; set; } = "fix leaks";
|
||||
public override Identifier Identifier { get; set; } = "fix leaks".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
+25
-26
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveGetItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "get item";
|
||||
public override Identifier Identifier { get; set; } = "get item".ToIdentifier();
|
||||
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
public override bool AllowMultipleInstances => true;
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
public float TargetCondition { get; set; } = 1;
|
||||
public bool AllowDangerousPressure { get; set; }
|
||||
|
||||
public readonly ImmutableArray<string> IdentifiersOrTags;
|
||||
public readonly ImmutableArray<Identifier> IdentifiersOrTags;
|
||||
|
||||
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs)
|
||||
private bool spawnItemIfNotFound = false;
|
||||
@@ -32,8 +32,8 @@ namespace Barotrauma
|
||||
private bool isDoneSeeking;
|
||||
public Item TargetItem => targetItem;
|
||||
private int currSearchIndex;
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
public string[] ignoredIdentifiersOrTags;
|
||||
public Identifier[] ignoredContainerIdentifiers;
|
||||
public Identifier[] ignoredIdentifiersOrTags;
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private float currItemPriority;
|
||||
private readonly bool checkInventory;
|
||||
@@ -83,10 +83,10 @@ namespace Barotrauma
|
||||
moveToTarget = targetItem?.GetRootInventoryOwner();
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, string identifierOrTag, AIObjectiveManager objectiveManager, bool equip = true, bool checkInventory = true, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: this(character, new string[] { identifierOrTag }, objectiveManager, equip, checkInventory, priorityModifier, spawnItemIfNotFound) { }
|
||||
public AIObjectiveGetItem(Character character, Identifier identifierOrTag, AIObjectiveManager objectiveManager, bool equip = true, bool checkInventory = true, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: this(character, new Identifier[] { identifierOrTag }, objectiveManager, equip, checkInventory, priorityModifier, spawnItemIfNotFound) { }
|
||||
|
||||
public AIObjectiveGetItem(Character character, IEnumerable<string> identifiersOrTags, AIObjectiveManager objectiveManager, bool equip = true, bool checkInventory = true, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
public AIObjectiveGetItem(Character character, IEnumerable<Identifier> identifiersOrTags, AIObjectiveManager objectiveManager, bool equip = true, bool checkInventory = true, float priorityModifier = 1, bool spawnItemIfNotFound = false)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
currSearchIndex = -1;
|
||||
@@ -97,27 +97,27 @@ namespace Barotrauma
|
||||
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray();
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ParseGearTags(IEnumerable<string> identifiersOrTags)
|
||||
public static IEnumerable<Identifier> ParseGearTags(IEnumerable<Identifier> identifiersOrTags)
|
||||
{
|
||||
var tags = new List<string>();
|
||||
foreach (string tag in identifiersOrTags)
|
||||
var tags = new List<Identifier>();
|
||||
foreach (Identifier tag in identifiersOrTags)
|
||||
{
|
||||
if (!tag.Contains('!'))
|
||||
if (!tag.Contains("!"))
|
||||
{
|
||||
tags.Add(tag.ToLowerInvariant());
|
||||
tags.Add(tag);
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ParseIgnoredTags(IEnumerable<string> identifiersOrTags)
|
||||
public static IEnumerable<Identifier> ParseIgnoredTags(IEnumerable<Identifier> identifiersOrTags)
|
||||
{
|
||||
var ignoredTags = new List<string>();
|
||||
foreach (string tag in identifiersOrTags)
|
||||
var ignoredTags = new List<Identifier>();
|
||||
foreach (Identifier tag in identifiersOrTags)
|
||||
{
|
||||
if (tag.Contains('!'))
|
||||
if (tag.Contains("!"))
|
||||
{
|
||||
ignoredTags.Add(tag.Remove("!").ToLowerInvariant());
|
||||
ignoredTags.Add(tag.Remove("!"));
|
||||
}
|
||||
}
|
||||
return ignoredTags;
|
||||
@@ -177,7 +177,7 @@ namespace Barotrauma
|
||||
if (dangerousPressure)
|
||||
{
|
||||
#if DEBUG
|
||||
string itemName = targetItem != null ? targetItem.Name : IdentifiersOrTags.FirstOrDefault();
|
||||
string itemName = targetItem != null ? targetItem.Name : IdentifiersOrTags.FirstOrDefault().Value;
|
||||
DebugConsole.NewMessage($"{character.Name}: Seeking item ({itemName}) aborted, because the pressure is dangerous.", Color.Yellow);
|
||||
#endif
|
||||
Abandon = true;
|
||||
@@ -480,7 +480,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(prefab, character.Inventory, onSpawned: (Item spawnedItem) =>
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, character.Inventory, onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
targetItem = spawnedItem;
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && (character.Submarine?.Info.IsOutpost ?? false))
|
||||
@@ -528,14 +528,13 @@ namespace Barotrauma
|
||||
|
||||
private bool CheckItem(Item item)
|
||||
{
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (item.IsThisOrAnyContainerIgnoredByAI(character)) { return false; }
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
if (ignoredItems.Contains(item)) { return false; };
|
||||
if (ignoredIdentifiersOrTags != null && ignoredIdentifiersOrTags.Any(id => item.prefab.Identifier == id || item.HasTag(id))) { return false; }
|
||||
if (ignoredIdentifiersOrTags != null && ignoredIdentifiersOrTags.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
|
||||
if (item.Condition < TargetCondition) { return false; }
|
||||
if (ItemFilter != null && !ItemFilter(item)) { return false; }
|
||||
if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; }
|
||||
return IdentifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id));
|
||||
return IdentifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && item.Prefab.VariantOf == id));
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
@@ -575,9 +574,9 @@ namespace Barotrauma
|
||||
if (!character.IsOnPlayerTeam) { return; }
|
||||
if (objectiveManager.CurrentOrder != objectiveManager.CurrentObjective) { return; }
|
||||
if (CannotFindDialogueCondition != null && !CannotFindDialogueCondition()) { return; }
|
||||
string msg = TextManager.Get(CannotFindDialogueIdentifierOverride, returnNull: true) ?? TextManager.Get("dialogcannotfinditem", returnNull: true);
|
||||
if (msg == null) { return; }
|
||||
character.Speak(msg, identifier: "dialogcannotfinditem", minDurationBetweenSimilar: 20.0f);
|
||||
LocalizedString msg = TextManager.Get(CannotFindDialogueIdentifierOverride, "dialogcannotfinditem");
|
||||
if (msg.IsNullOrEmpty() || !msg.Loaded) { return; }
|
||||
character.Speak(msg.Value, identifier: "dialogcannotfinditem".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveGetItems : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "get items";
|
||||
public override Identifier Identifier { get; set; } = "get items".ToIdentifier();
|
||||
public override string DebugTag => $"{Identifier}";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowMultipleInstances => true;
|
||||
@@ -24,13 +24,13 @@ namespace Barotrauma
|
||||
public bool RequireLoaded { get; set; }
|
||||
public bool RequireAllItems { get; set; }
|
||||
|
||||
private readonly ImmutableArray<string> gearTags;
|
||||
private readonly string[] ignoredTags;
|
||||
private readonly ImmutableArray<Identifier> gearTags;
|
||||
private readonly Identifier[] ignoredTags;
|
||||
private bool subObjectivesCreated;
|
||||
|
||||
public readonly HashSet<Item> achievedItems = new HashSet<Item>();
|
||||
|
||||
public AIObjectiveGetItems(Character character, AIObjectiveManager objectiveManager, IEnumerable<string> identifiersOrTags, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||
public AIObjectiveGetItems(Character character, AIObjectiveManager objectiveManager, IEnumerable<Identifier> identifiersOrTags, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
gearTags = AIObjectiveGetItem.ParseGearTags(identifiersOrTags).ToImmutableArray();
|
||||
ignoredTags = AIObjectiveGetItem.ParseIgnoredTags(identifiersOrTags).ToArray();
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!subObjectivesCreated)
|
||||
{
|
||||
foreach (string tag in gearTags)
|
||||
foreach (Identifier tag in gearTags)
|
||||
{
|
||||
if (subObjectives.Any(so => so is AIObjectiveGetItem getItem && getItem.IdentifiersOrTags.Contains(tag))) { continue; }
|
||||
int count = gearTags.Count(t => t == tag);
|
||||
|
||||
+48
-35
@@ -8,7 +8,9 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveGoTo : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "go to";
|
||||
public override Identifier Identifier { get; set; } = "go to".ToIdentifier();
|
||||
|
||||
public override bool KeepDivingGearOn => GetTargetHull() == null;
|
||||
|
||||
private AIObjectiveFindDivingGear findDivingGear;
|
||||
private readonly bool repeat;
|
||||
@@ -73,14 +75,6 @@ namespace Barotrauma
|
||||
_closeEnough = Math.Max(minDistance, value);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Currently we never check the visibility (to the end node), which is actually unintentional.
|
||||
// I don't think it has caused any issues so far, so let's keep defaulting to false for now, because the less we do raycasts the better.
|
||||
// However, if there are cases where the bots attempt to go through walls (select the end node that is behind an obstacle), we should set this true.
|
||||
|
||||
// NOTE: This seemes to have caused an issue now Regalis11/Barotrauma#8067: namely, the bot was trying to use a waypoint that was obstructed by a shuttle
|
||||
// because obstruction was only checked when checking visibility in PathFinder. Changed that so that obstructed nodes are no longer used.
|
||||
public bool CheckVisibility { get; set; }
|
||||
public bool IgnoreIfTargetDead { get; set; }
|
||||
public bool AllowGoingOutside { get; set; }
|
||||
|
||||
@@ -96,8 +90,8 @@ namespace Barotrauma
|
||||
public override bool AllowOutsideSubmarine => AllowGoingOutside;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public string DialogueIdentifier { get; set; } = "dialogcannotreachtarget";
|
||||
public string TargetName { get; set; }
|
||||
public Identifier DialogueIdentifier { get; set; } = "dialogcannotreachtarget".ToIdentifier();
|
||||
public LocalizedString TargetName { get; set; }
|
||||
|
||||
public ISpatialEntity Target { get; private set; }
|
||||
|
||||
@@ -180,9 +174,11 @@ namespace Barotrauma
|
||||
if (DialogueIdentifier == null) { return; }
|
||||
if (!SpeakIfFails) { return; }
|
||||
if (SpeakCannotReachCondition != null && !SpeakCannotReachCondition()) { return; }
|
||||
string msg = TargetName == null ? TextManager.Get(DialogueIdentifier, true) : TextManager.GetWithVariable(DialogueIdentifier, "[name]", TargetName, formatCapitals: !(Target is Character));
|
||||
if (msg == null) { return; }
|
||||
character.Speak(msg, identifier: DialogueIdentifier, minDurationBetweenSimilar: 20.0f);
|
||||
LocalizedString msg = TargetName == null ?
|
||||
TextManager.Get(DialogueIdentifier) :
|
||||
TextManager.GetWithVariable(DialogueIdentifier, "[name]".ToIdentifier(), TargetName, formatCapitals: Target is Character ? FormatCapitals.No : FormatCapitals.Yes);
|
||||
if (msg.IsNullOrEmpty() || !msg.Loaded) { return; }
|
||||
character.Speak(msg.Value, identifier: DialogueIdentifier, minDurationBetweenSimilar: 20.0f);
|
||||
}
|
||||
|
||||
public void ForceAct(float deltaTime) => Act(deltaTime);
|
||||
@@ -265,15 +261,15 @@ namespace Barotrauma
|
||||
{
|
||||
Character followTarget = Target as Character;
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit)) && character.NeedsAir;
|
||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit));
|
||||
if (Mimic)
|
||||
{
|
||||
if (HumanAIController.HasDivingSuit(followTarget) && character.NeedsAir)
|
||||
if (HumanAIController.HasDivingSuit(followTarget))
|
||||
{
|
||||
needsDivingGear = true;
|
||||
needsDivingSuit = true;
|
||||
}
|
||||
else if (HumanAIController.HasDivingMask(followTarget) && character.NeedsAir)
|
||||
else if (HumanAIController.HasDivingMask(followTarget))
|
||||
{
|
||||
needsDivingGear = true;
|
||||
}
|
||||
@@ -382,13 +378,23 @@ namespace Barotrauma
|
||||
{
|
||||
useScooter = false;
|
||||
checkScooterTimer = checkScooterTime * Rand.Range(0.75f, 1.25f);
|
||||
string scooterTag = "scooter";
|
||||
string batteryTag = "mobilebattery";
|
||||
Identifier scooterTag = "scooter".ToIdentifier();
|
||||
Identifier batteryTag = "mobilebattery".ToIdentifier();
|
||||
Item scooter = null;
|
||||
float closeEnough = 250;
|
||||
float squaredDistance = Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition);
|
||||
bool shouldUseScooter = squaredDistance > closeEnough * closeEnough && (!Mimic ||
|
||||
(targetCharacter != null && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false)) || squaredDistance > Math.Pow(closeEnough * 2, 2));
|
||||
bool shouldUseScooter = Mimic && targetCharacter != null && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false);
|
||||
if (!shouldUseScooter)
|
||||
{
|
||||
float threshold = 500;
|
||||
if (isInside)
|
||||
{
|
||||
Vector2 diff = Target.WorldPosition - character.WorldPosition;
|
||||
shouldUseScooter = Math.Abs(diff.X) > threshold || Math.Abs(diff.Y) > 150;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldUseScooter = Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) > threshold * threshold;
|
||||
}
|
||||
}
|
||||
if (HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> equippedScooters, recursive: false, requireEquipped: true))
|
||||
{
|
||||
// Currently equipped scooter
|
||||
@@ -424,8 +430,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isScooterEquipped = scooter != null && character.HasEquippedItem(scooter);
|
||||
if (scooter != null && isScooterEquipped)
|
||||
if (scooter != null && character.HasEquippedItem(scooter))
|
||||
{
|
||||
if (shouldUseScooter)
|
||||
{
|
||||
@@ -493,7 +498,7 @@ namespace Barotrauma
|
||||
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
|
||||
endNodeFilter: endNodeFilter,
|
||||
nodeFilter: nodeFilter,
|
||||
checkVisiblity: CheckVisibility);
|
||||
checkVisiblity: Target is Item || Target is Character);
|
||||
}
|
||||
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
|
||||
{
|
||||
@@ -534,6 +539,7 @@ namespace Barotrauma
|
||||
|
||||
void UseScooter(Vector2 targetWorldPos)
|
||||
{
|
||||
if (!character.HasEquippedItem("scooter".ToIdentifier())) { return; }
|
||||
SteeringManager.Reset();
|
||||
character.CursorPosition = targetWorldPos;
|
||||
if (character.Submarine != null)
|
||||
@@ -542,19 +548,26 @@ namespace Barotrauma
|
||||
}
|
||||
Vector2 diff = character.CursorPosition - character.Position;
|
||||
Vector2 dir = Vector2.Normalize(diff);
|
||||
float sqrDist = diff.LengthSquared();
|
||||
if (sqrDist > MathUtils.Pow2(CloseEnough * 1.5f))
|
||||
if (character.CurrentHull == null && IsFollowOrderObjective)
|
||||
{
|
||||
SteeringManager.SteeringManual(1.0f, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
float dot = Vector2.Dot(dir, VectorExtensions.Forward(character.AnimController.Collider.Rotation + MathHelper.PiOver2));
|
||||
bool isFacing = dot > 0.9f;
|
||||
if (!isFacing && sqrDist > MathUtils.Pow2(CloseEnough))
|
||||
float sqrDist = diff.LengthSquared();
|
||||
if (sqrDist > MathUtils.Pow2(CloseEnough * 1.5f))
|
||||
{
|
||||
SteeringManager.SteeringManual(1.0f, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
float dot = Vector2.Dot(dir, VectorExtensions.Forward(character.AnimController.Collider.Rotation + MathHelper.PiOver2));
|
||||
bool isFacing = dot > 0.9f;
|
||||
if (!isFacing && sqrDist > MathUtils.Pow2(CloseEnough))
|
||||
{
|
||||
SteeringManager.SteeringManual(1.0f, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringManual(1.0f, dir);
|
||||
}
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveIdle : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "idle";
|
||||
public override Identifier Identifier { get; set; } = "idle".ToIdentifier();
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + Environment.StackTrace.CleanupStackTrace()); }
|
||||
|
||||
public readonly HashSet<string> PreferredOutpostModuleTypes = new HashSet<string>();
|
||||
public readonly HashSet<Identifier> PreferredOutpostModuleTypes = new HashSet<Identifier>();
|
||||
|
||||
public void CalculatePriority(float max = 0)
|
||||
{
|
||||
@@ -391,7 +391,7 @@ namespace Barotrauma
|
||||
{
|
||||
targetHulls.Clear();
|
||||
hullWeights.Clear();
|
||||
foreach (var hull in Hull.hullList)
|
||||
foreach (var hull in Hull.HullList)
|
||||
{
|
||||
if (character.Submarine == null) { break; }
|
||||
if (HumanAIController.UnsafeHulls.Contains(hull)) { continue; }
|
||||
|
||||
+10
-12
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveLoadItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "load item";
|
||||
public override Identifier Identifier { get; set; } = "load item".ToIdentifier();
|
||||
public override bool IsLoop
|
||||
{
|
||||
get => true;
|
||||
@@ -20,9 +20,9 @@ namespace Barotrauma
|
||||
private AIObjectiveLoadItems.ItemCondition TargetItemCondition { get; }
|
||||
private Item Container { get; }
|
||||
private ItemContainer ItemContainer { get; }
|
||||
private ImmutableArray<string> TargetContainerTags { get; }
|
||||
private ImmutableHashSet<string> ValidContainableItemIdentifiers { get; }
|
||||
private static Dictionary<ItemPrefab, ImmutableHashSet<string>> AllValidContainableItemIdentifiers { get; } = new Dictionary<ItemPrefab, ImmutableHashSet<string>>();
|
||||
private ImmutableArray<Identifier> TargetContainerTags { get; }
|
||||
private ImmutableHashSet<Identifier> ValidContainableItemIdentifiers { get; }
|
||||
private static Dictionary<ItemPrefab, ImmutableHashSet<Identifier>> AllValidContainableItemIdentifiers { get; } = new Dictionary<ItemPrefab, ImmutableHashSet<Identifier>>();
|
||||
|
||||
private int itemIndex = 0;
|
||||
private AIObjectiveDecontainItem decontainObjective;
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
private Item targetItem;
|
||||
private readonly string abandonGetItemDialogueIdentifier = "dialogcannotfindloadable";
|
||||
|
||||
public AIObjectiveLoadItem(Item container, ImmutableArray<string> targetTags, AIObjectiveLoadItems.ItemCondition targetCondition, string option, Character character, AIObjectiveManager objectiveManager, float priorityModifier)
|
||||
public AIObjectiveLoadItem(Item container, ImmutableArray<Identifier> targetTags, AIObjectiveLoadItems.ItemCondition targetCondition, Identifier option, Character character, AIObjectiveManager objectiveManager, float priorityModifier)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
Container = container;
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
}
|
||||
TargetContainerTags = targetTags;
|
||||
TargetItemCondition = targetCondition;
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
if (!option.IsEmpty)
|
||||
{
|
||||
string optionSpecificDialogueIdentifier = $"{abandonGetItemDialogueIdentifier}.{option}";
|
||||
if (TextManager.ContainsTag(optionSpecificDialogueIdentifier))
|
||||
@@ -63,7 +63,7 @@ namespace Barotrauma
|
||||
|
||||
private enum CheckStatus { Unfinished, Finished }
|
||||
|
||||
private ImmutableHashSet<string> GetValidContainableItemIdentifiers()
|
||||
private ImmutableHashSet<Identifier> GetValidContainableItemIdentifiers()
|
||||
{
|
||||
if (AllValidContainableItemIdentifiers.TryGetValue(Container.Prefab, out var existingIdentifiers))
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace Barotrauma
|
||||
var potentialContainablePrefabs = MapEntityPrefab.List
|
||||
.Where(mep => mep is ItemPrefab ip && ItemContainer.ContainableItemIdentifiers.Any(i => i == ip.Identifier || ip.Tags.Contains(i)))
|
||||
.Cast<ItemPrefab>();
|
||||
var validContainableItemIdentifiers = new HashSet<string>();
|
||||
var validContainableItemIdentifiers = new HashSet<Identifier>();
|
||||
foreach (var component in Container.Components)
|
||||
{
|
||||
if (CheckComponent() == CheckStatus.Finished)
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
useDefaultContainableItemIdentifiers = false;
|
||||
if (statusEffect.TargetIdentifiers != null)
|
||||
{
|
||||
foreach (string target in statusEffect.TargetIdentifiers)
|
||||
foreach (Identifier target in statusEffect.TargetIdentifiers)
|
||||
{
|
||||
foreach (var prefab in potentialContainablePrefabs)
|
||||
{
|
||||
@@ -308,11 +308,9 @@ namespace Barotrauma
|
||||
if (rootInventoryOwner is Item parentItem)
|
||||
{
|
||||
if (parentItem.HasTag("donttakeitems")) { return false; }
|
||||
if (!(parentItem.GetComponent<ItemContainer>()?.HasAccess(character) ?? true)) { return false; }
|
||||
}
|
||||
if (item.IsThisOrAnyContainerIgnoredByAI(character)) { return false; }
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
if (!character.HasItem(item) && !CanEquip(item)) { return false; }
|
||||
if (!ItemContainer.HasAccess(character)) { return false; }
|
||||
if (!ItemContainer.CanBeContained(item)) { return false; }
|
||||
if (AIObjectiveLoadItems.ItemMatchesTargetCondition(item, TargetItemCondition)) { return false; }
|
||||
if (TargetItemCondition == AIObjectiveLoadItems.ItemCondition.Full)
|
||||
|
||||
+7
-8
@@ -9,11 +9,11 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveLoadItems : AIObjectiveLoop<Item>
|
||||
{
|
||||
public override string Identifier { get; set; } = "load items";
|
||||
public override Identifier Identifier { get; set; } = "load items".ToIdentifier();
|
||||
protected override float IgnoreListClearInterval => 20.0f;
|
||||
protected override bool ResetWhenClearingIgnoreList => false;
|
||||
|
||||
private ImmutableArray<string> TargetContainerTags { get; }
|
||||
private ImmutableArray<Identifier> TargetContainerTags { get; }
|
||||
private List<Item> TargetContainers { get; } = new List<Item>();
|
||||
private ItemCondition TargetCondition { get; }
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Barotrauma
|
||||
Full
|
||||
}
|
||||
|
||||
public AIObjectiveLoadItems(Character character, AIObjectiveManager objectiveManager, string option, ImmutableArray<string> containerTags, Item targetContainer = null, float priorityModifier = 1)
|
||||
public AIObjectiveLoadItems(Character character, AIObjectiveManager objectiveManager, Identifier option, ImmutableArray<Identifier> containerTags, Item targetContainer = null, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier, option)
|
||||
{
|
||||
if ((containerTags == null || containerTags.None()) && targetContainer == null)
|
||||
@@ -50,19 +50,18 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsValidTarget(Item item, Character character, ImmutableArray<string>? targetContainerTags = null, ItemCondition? targetCondition = null)
|
||||
public static bool IsValidTarget(Item item, Character character, ImmutableArray<Identifier>? targetContainerTags = null, ItemCondition? targetCondition = null)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (item.Removed) { return false; }
|
||||
if (targetContainerTags.HasValue && !Order.TargetItemsMatchItem(targetContainerTags.Value, item)) { return false; }
|
||||
if (targetContainerTags.HasValue && !OrderPrefab.TargetItemsMatchItem(targetContainerTags.Value, item)) { return false; }
|
||||
if (!(item.GetComponent<ItemContainer>() is ItemContainer container)) { return false; }
|
||||
if (container.Inventory == null) { return false; }
|
||||
if (targetCondition.HasValue && container.Inventory.IsFull() && container.Inventory.AllItems.None(i => ItemMatchesTargetCondition(i, targetCondition.Value))) { return false; }
|
||||
if (!AIObjectiveCleanupItems.IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||
if (item.GetRootInventoryOwner() is Character owner && owner != character) { return false; }
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (item.IsThisOrAnyContainerIgnoredByAI(character)) { return false; }
|
||||
if (!container.HasAccess(character)) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { return false; }
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
// Ignore items that require power but don't have it
|
||||
if (item.GetComponent<Powered>() is Powered powered && powered.PowerConsumption > 0 && powered.Voltage < powered.MinVoltage) { return false; }
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
public AIObjectiveLoop(Character character, AIObjectiveManager objectiveManager, float priorityModifier, string option = null)
|
||||
public AIObjectiveLoop(Character character, AIObjectiveManager objectiveManager, float priorityModifier, Identifier option = default)
|
||||
: base(character, objectiveManager, priorityModifier, option) { }
|
||||
|
||||
protected override void Act(float deltaTime) { }
|
||||
|
||||
+80
-79
@@ -10,6 +10,16 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveManager
|
||||
{
|
||||
public enum ObjectiveType
|
||||
{
|
||||
None = 0,
|
||||
Order = 1,
|
||||
Objective = 2,
|
||||
|
||||
MinValue = 0,
|
||||
MaxValue = 2
|
||||
}
|
||||
|
||||
public const float HighestOrderPriority = 70;
|
||||
public const float LowestOrderPriority = 60;
|
||||
public const float RunPriority = 50;
|
||||
@@ -38,7 +48,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public List<OrderInfo> CurrentOrders { get; } = new List<OrderInfo>();
|
||||
public List<Order> CurrentOrders { get; } = new List<Order>();
|
||||
/// <summary>
|
||||
/// The AIObjective in <see cref="CurrentOrders"/> with the highest <see cref="AIObjective.Priority"/>
|
||||
/// </summary>
|
||||
@@ -132,23 +142,23 @@ namespace Barotrauma
|
||||
int objectiveCount = Objectives.Count;
|
||||
foreach (var autonomousObjective in character.Info.Job.Prefab.AutonomousObjectives)
|
||||
{
|
||||
var orderPrefab = Order.GetPrefab(autonomousObjective.identifier);
|
||||
if (orderPrefab == null) { throw new Exception($"Could not find a matching prefab by the identifier: '{autonomousObjective.identifier}'"); }
|
||||
var orderPrefab = OrderPrefab.Prefabs[autonomousObjective.Identifier];
|
||||
if (orderPrefab == null) { throw new Exception($"Could not find a matching prefab by the identifier: '{autonomousObjective.Identifier}'"); }
|
||||
Item item = null;
|
||||
if (orderPrefab.MustSetTarget)
|
||||
{
|
||||
item = orderPrefab.GetMatchingItems(character.Submarine, mustBelongToPlayerSub: false, requiredTeam: character.Info.TeamID, interactableFor: character, orderOption: autonomousObjective.option)?.GetRandom();
|
||||
item = orderPrefab.GetMatchingItems(character.Submarine, mustBelongToPlayerSub: false, requiredTeam: character.Info.TeamID, interactableFor: character)?.GetRandomUnsynced();
|
||||
}
|
||||
var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity, orderPrefab.GetTargetItemComponent(item), orderGiver: character);
|
||||
var order = new Order(orderPrefab, autonomousObjective.Option, item ?? character.CurrentHull as Entity, orderPrefab.GetTargetItemComponent(item), orderGiver: character);
|
||||
if (order == null) { continue; }
|
||||
if ((order.IgnoreAtOutpost || autonomousObjective.ignoreAtOutpost) && Level.IsLoadedOutpost && character.TeamID != CharacterTeamType.FriendlyNPC)
|
||||
if ((order.IgnoreAtOutpost || autonomousObjective.IgnoreAtOutpost) && Level.IsLoadedOutpost && character.TeamID != CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.DockedTo.None(s => s.TeamID != CharacterTeamType.FriendlyNPC && s.TeamID != character.TeamID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var objective = CreateObjective(order, autonomousObjective.option, character, autonomousObjective.priorityModifier);
|
||||
var objective = CreateObjective(order, autonomousObjective.PriorityModifier);
|
||||
if (objective != null && objective.CanBeCompleted)
|
||||
{
|
||||
AddObjective(objective, delay: Rand.Value() / 2);
|
||||
@@ -193,28 +203,20 @@ namespace Barotrauma
|
||||
{
|
||||
var previousObjective = CurrentObjective;
|
||||
var firstObjective = Objectives.FirstOrDefault();
|
||||
|
||||
bool currentObjectiveIsOrder = CurrentOrder != null && firstObjective != null && CurrentOrder.Priority > firstObjective.Priority;
|
||||
if (currentObjectiveIsOrder)
|
||||
|
||||
CurrentObjective = currentObjectiveIsOrder ? CurrentOrder : firstObjective;
|
||||
|
||||
if (previousObjective == CurrentObjective) { return CurrentObjective; }
|
||||
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority(Math.Max(CurrentObjective.Priority - 10, 0));
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
CurrentObjective = CurrentOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentObjective = firstObjective;
|
||||
}
|
||||
if (previousObjective != CurrentObjective)
|
||||
{
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority(Math.Max(CurrentObjective.Priority - 10, 0));
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ObjectiveManagerState,
|
||||
currentObjectiveIsOrder ? "order" : "objective"
|
||||
});
|
||||
}
|
||||
GameMain.NetworkMember.CreateEntityEvent(character,
|
||||
new Character.ObjectiveManagerStateEventData(currentObjectiveIsOrder ? ObjectiveType.Order : ObjectiveType.Objective));
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
@@ -333,7 +335,7 @@ namespace Barotrauma
|
||||
SortObjectives();
|
||||
}
|
||||
|
||||
public void SetOrder(Order order, string option, int priority, Character orderGiver, bool speak)
|
||||
public void SetOrder(Order order, bool speak)
|
||||
{
|
||||
if (character.IsDead)
|
||||
{
|
||||
@@ -345,13 +347,13 @@ namespace Barotrauma
|
||||
}
|
||||
ClearIgnored();
|
||||
|
||||
if (order == null || order.Identifier == "dismissed")
|
||||
if (order == null || order.IsDismissal)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
if (order.Option != Identifier.Empty)
|
||||
{
|
||||
if (CurrentOrders.Any(o => o.MatchesDismissedOrder(option)))
|
||||
if (CurrentOrders.Any(o => o.MatchesDismissedOrder(order.Option)))
|
||||
{
|
||||
var dismissedOrderInfo = CurrentOrders.First(o => o.MatchesDismissedOrder(option));
|
||||
var dismissedOrderInfo = CurrentOrders.First(o => o.MatchesDismissedOrder(order.Option));
|
||||
CurrentOrders.Remove(dismissedOrderInfo);
|
||||
}
|
||||
}
|
||||
@@ -366,18 +368,18 @@ namespace Barotrauma
|
||||
{
|
||||
if (CurrentOrders.Count <= i) { break; }
|
||||
var currentOrder = CurrentOrders[i];
|
||||
if (currentOrder.Objective == null || currentOrder.MatchesOrder(order, option))
|
||||
if (currentOrder.Objective == null || currentOrder.MatchesOrder(order))
|
||||
{
|
||||
CurrentOrders.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
var currentOrderInfo = character.GetCurrentOrder(currentOrder.Order, currentOrder.OrderOption);
|
||||
if (currentOrderInfo.HasValue)
|
||||
var currentOrderInfo = character.GetCurrentOrder(currentOrder);
|
||||
if (currentOrderInfo is Order)
|
||||
{
|
||||
int currentPriority = currentOrderInfo.Value.ManualPriority;
|
||||
int currentPriority = currentOrderInfo.ManualPriority;
|
||||
if (currentOrder.ManualPriority != currentPriority)
|
||||
{
|
||||
CurrentOrders[i] = new OrderInfo(currentOrder, currentPriority);
|
||||
CurrentOrders[i] = currentOrder.WithManualPriority(currentPriority);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -386,46 +388,46 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var newCurrentOrder = CreateObjective(order, option, orderGiver);
|
||||
if (newCurrentOrder != null)
|
||||
var newCurrentObjective = CreateObjective(order);
|
||||
if (newCurrentObjective != null)
|
||||
{
|
||||
newCurrentOrder.Abandoned += () => DismissSelf(order, option);
|
||||
CurrentOrders.Add(new OrderInfo(order, option, priority, newCurrentOrder));
|
||||
newCurrentObjective.Abandoned += () => DismissSelf(order);
|
||||
CurrentOrders.Add(order.WithObjective(newCurrentObjective));
|
||||
}
|
||||
if (!HasOrders())
|
||||
{
|
||||
// Recreate objectives, because some of them may be removed, if impossible to complete (e.g. due to path finding)
|
||||
CreateAutonomousObjectives();
|
||||
}
|
||||
else if (newCurrentOrder != null)
|
||||
else if (newCurrentObjective != null)
|
||||
{
|
||||
if (speak && character.IsOnPlayerTeam)
|
||||
{
|
||||
string msg = newCurrentOrder.IsAllowed ? TextManager.Get("DialogAffirmative") : TextManager.Get("DialogNegative");
|
||||
character.Speak(msg, delay: 1.0f);
|
||||
LocalizedString msg = newCurrentObjective.IsAllowed ? TextManager.Get("DialogAffirmative") : TextManager.Get("DialogNegative");
|
||||
character.Speak(msg.Value, delay: 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AIObjective CreateObjective(Order order, string option, Character orderGiver, float priorityModifier = 1)
|
||||
public AIObjective CreateObjective(Order order, float priorityModifier = 1)
|
||||
{
|
||||
if (order == null || order.Identifier == "dismissed") { return null; }
|
||||
if (order == null || order.IsDismissal) { return null; }
|
||||
AIObjective newObjective;
|
||||
switch (order.Identifier.ToLowerInvariant())
|
||||
switch (order.Identifier.Value.ToLowerInvariant())
|
||||
{
|
||||
case "follow":
|
||||
if (orderGiver == null) { return null; }
|
||||
newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||
if (order.OrderGiver == null) { return null; }
|
||||
newObjective = new AIObjectiveGoTo(order.OrderGiver, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||
{
|
||||
CloseEnough = Rand.Range(80f, 100f),
|
||||
CloseEnoughMultiplier = Math.Min(1 + HumanAIController.CountCrew(c => c.ObjectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Target == orderGiver), onlyBots: true) * Rand.Range(0.8f, 1f), 4),
|
||||
CloseEnoughMultiplier = Math.Min(1 + HumanAIController.CountCrew(c => c.ObjectiveManager.HasOrder<AIObjectiveGoTo>(o => o.Target == order.OrderGiver), onlyBots: true) * Rand.Range(0.8f, 1f), 4),
|
||||
ExtraDistanceOutsideSub = 100,
|
||||
ExtraDistanceWhileSwimming = 100,
|
||||
AllowGoingOutside = true,
|
||||
IgnoreIfTargetDead = true,
|
||||
IsFollowOrderObjective = true,
|
||||
Mimic = character.IsOnPlayerTeam,
|
||||
DialogueIdentifier = "dialogcannotreachplace"
|
||||
DialogueIdentifier = "dialogcannotreachplace".ToIdentifier()
|
||||
};
|
||||
break;
|
||||
case "wait":
|
||||
@@ -435,14 +437,14 @@ namespace Barotrauma
|
||||
};
|
||||
break;
|
||||
case "return":
|
||||
newObjective = new AIObjectiveReturn(character, orderGiver, this, priorityModifier: priorityModifier);
|
||||
newObjective.Completed += () => DismissSelf(order, option);
|
||||
newObjective = new AIObjectiveReturn(character, order.OrderGiver, this, priorityModifier: priorityModifier);
|
||||
newObjective.Completed += () => DismissSelf(order);
|
||||
break;
|
||||
case "fixleaks":
|
||||
newObjective = new AIObjectiveFixLeaks(character, this, priorityModifier: priorityModifier, prioritizedHull: order.TargetEntity as Hull);
|
||||
break;
|
||||
case "chargebatteries":
|
||||
newObjective = new AIObjectiveChargeBatteries(character, this, option, priorityModifier);
|
||||
newObjective = new AIObjectiveChargeBatteries(character, this, order.Option, priorityModifier);
|
||||
break;
|
||||
case "rescue":
|
||||
newObjective = new AIObjectiveRescueAll(character, this, priorityModifier);
|
||||
@@ -459,16 +461,16 @@ namespace Barotrauma
|
||||
if (order.TargetItemComponent is Pump targetPump)
|
||||
{
|
||||
if (!order.TargetItemComponent.Item.IsInteractable(character)) { return null; }
|
||||
newObjective = new AIObjectiveOperateItem(targetPump, character, this, option, false, priorityModifier: priorityModifier)
|
||||
newObjective = new AIObjectiveOperateItem(targetPump, character, this, order.Option, false, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = false,
|
||||
Override = orderGiver != null && orderGiver.IsCommanding
|
||||
Override = order.OrderGiver is { IsCommanding: true }
|
||||
};
|
||||
newObjective.Completed += () => DismissSelf(order, option);
|
||||
newObjective.Completed += () => DismissSelf(order);
|
||||
}
|
||||
else
|
||||
{
|
||||
newObjective = new AIObjectivePumpWater(character, this, option, priorityModifier: priorityModifier);
|
||||
newObjective = new AIObjectivePumpWater(character, this, order.Option, priorityModifier: priorityModifier);
|
||||
}
|
||||
break;
|
||||
case "extinguishfires":
|
||||
@@ -488,22 +490,22 @@ namespace Barotrauma
|
||||
if (steering != null) { steering.PosToMaintain = steering.Item.Submarine?.WorldPosition; }
|
||||
if (order.TargetItemComponent == null) { return null; }
|
||||
if (!order.TargetItemComponent.Item.IsInteractable(character)) { return null; }
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option,
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, order.Option,
|
||||
requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && orderGiver.IsCommanding
|
||||
Override = order.OrderGiver != null && order.OrderGiver.IsCommanding
|
||||
};
|
||||
break;
|
||||
case "setchargepct":
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, false, priorityModifier: priorityModifier)
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, order.Option, false, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = false,
|
||||
Override = !character.IsDismissed,
|
||||
completionCondition = () =>
|
||||
{
|
||||
if (float.TryParse(option, out float pct))
|
||||
if (float.TryParse(order.Option.Value, out float pct))
|
||||
{
|
||||
var targetRatio = Math.Clamp(pct, 0f, 1f);
|
||||
var currentRatio = (order.TargetItemComponent as PowerContainer).RechargeRatio;
|
||||
@@ -541,7 +543,7 @@ namespace Barotrauma
|
||||
newObjective = new AIObjectiveEscapeHandcuffs(character, this, priorityModifier: priorityModifier);
|
||||
break;
|
||||
case "prepareforexpedition":
|
||||
newObjective = new AIObjectivePrepare(character, this, order.GetTargetItems(option), order.RequireItems)
|
||||
newObjective = new AIObjectivePrepare(character, this, order.GetTargetItems(order.Option), order.RequireItems)
|
||||
{
|
||||
KeepActiveWhenReady = true,
|
||||
CheckInventory = true,
|
||||
@@ -557,7 +559,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareObjective = new AIObjectivePrepare(character, this, order.GetTargetItems(option), order.RequireItems)
|
||||
prepareObjective = new AIObjectivePrepare(character, this, order.GetTargetItems(order.Option), order.RequireItems)
|
||||
{
|
||||
KeepActiveWhenReady = false,
|
||||
CheckInventory = false,
|
||||
@@ -568,20 +570,20 @@ namespace Barotrauma
|
||||
prepareObjective.KeepActiveWhenReady = false;
|
||||
prepareObjective.Equip = true;
|
||||
newObjective = prepareObjective;
|
||||
newObjective.Completed += () => DismissSelf(order, option);
|
||||
newObjective.Completed += () => DismissSelf(order);
|
||||
break;
|
||||
case "loaditems":
|
||||
newObjective = new AIObjectiveLoadItems(character, this, option, order.GetTargetItems(option), order.TargetEntity as Item, priorityModifier);
|
||||
newObjective = new AIObjectiveLoadItems(character, this, order.Option, order.GetTargetItems(order.Option), order.TargetEntity as Item, priorityModifier);
|
||||
break;
|
||||
default:
|
||||
if (order.TargetItemComponent == null) { return null; }
|
||||
if (!order.TargetItemComponent.Item.IsInteractable(character)) { return null; }
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option,
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, order.Option,
|
||||
requireEquip: false, useController: order.UseController, controller: order.ConnectedController, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = true,
|
||||
// Don't override unless it's an order by a player
|
||||
Override = orderGiver != null && orderGiver.IsCommanding
|
||||
Override = order.OrderGiver != null && order.OrderGiver.IsCommanding
|
||||
};
|
||||
if (newObjective.Abandon) { return null; }
|
||||
break;
|
||||
@@ -594,27 +596,26 @@ namespace Barotrauma
|
||||
return newObjective;
|
||||
}
|
||||
|
||||
private void DismissSelf(Order order, string option)
|
||||
private void DismissSelf(Order order)
|
||||
{
|
||||
var currentOrder = CurrentOrders.FirstOrDefault(oi => oi.MatchesOrder(order, option));
|
||||
if (currentOrder.Order == null)
|
||||
var currentOrder = CurrentOrders.FirstOrDefault(oi => oi.MatchesOrder(order.Identifier, order.Option));
|
||||
if (currentOrder == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Tried to self-dismiss an order, but no matching current order was found");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
Order dismissOrder = Order.GetPrefab("dismissed");
|
||||
var orderOption = Order.GetDismissOrderOption(currentOrder);
|
||||
int priority = currentOrder.ManualPriority;
|
||||
|
||||
Order dismissOrder = currentOrder.GetDismissal();
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.IsSinglePlayer)
|
||||
{
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(character, dismissOrder, orderOption, priority, character);
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(character, dismissOrder);
|
||||
}
|
||||
#else
|
||||
GameMain.Server?.SendOrderChatMessage(new OrderChatMessage(dismissOrder, orderOption, priority, currentOrder.Order.TargetSpatialEntity, character, character));
|
||||
SetOrder(dismissOrder, orderOption, priority, character, speak: false);
|
||||
GameMain.Server?.SendOrderChatMessage(new OrderChatMessage(dismissOrder, character, character));
|
||||
SetOrder(dismissOrder, speak: false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -638,7 +639,7 @@ namespace Barotrauma
|
||||
public bool IsActiveObjective<T>() where T : AIObjective => GetActiveObjective() is T;
|
||||
|
||||
public AIObjective GetActiveObjective() => CurrentObjective?.GetActiveObjective();
|
||||
public T GetOrder<T>() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T).Objective as T;
|
||||
public T GetOrder<T>() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T)?.Objective as T;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last active objective of the specific type.
|
||||
@@ -704,7 +705,7 @@ namespace Barotrauma
|
||||
return 0;
|
||||
}
|
||||
|
||||
public OrderInfo? GetCurrentOrderInfo()
|
||||
public Order GetCurrentOrderInfo()
|
||||
{
|
||||
if (currentOrder == null) { return null; }
|
||||
return CurrentOrders.FirstOrDefault(o => o.Objective == CurrentOrder);
|
||||
|
||||
+9
-4
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveOperateItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "operate item";
|
||||
public override Identifier Identifier { get; set; } = "operate item".ToIdentifier();
|
||||
public override string DebugTag => $"{Identifier} {component.Name}";
|
||||
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
@@ -67,6 +67,11 @@ namespace Barotrauma
|
||||
Priority = 0;
|
||||
return Priority;
|
||||
}
|
||||
else if (targetItem.IsClaimedByBallastFlora)
|
||||
{
|
||||
Priority = 0;
|
||||
return Priority;
|
||||
}
|
||||
var reactor = component?.Item.GetComponent<Reactor>();
|
||||
if (reactor != null)
|
||||
{
|
||||
@@ -79,7 +84,7 @@ namespace Barotrauma
|
||||
return Priority;
|
||||
}
|
||||
}
|
||||
switch (Option)
|
||||
switch (Option.Value.ToLowerInvariant())
|
||||
{
|
||||
case "shutdown":
|
||||
if (!reactor.PowerOn)
|
||||
@@ -146,7 +151,7 @@ namespace Barotrauma
|
||||
return Priority;
|
||||
}
|
||||
|
||||
public AIObjectiveOperateItem(ItemComponent item, Character character, AIObjectiveManager objectiveManager, string option, bool requireEquip,
|
||||
public AIObjectiveOperateItem(ItemComponent item, Character character, AIObjectiveManager objectiveManager, Identifier option, bool requireEquip,
|
||||
Entity operateTarget = null, bool useController = false, ItemComponent controller = null, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier, option)
|
||||
{
|
||||
@@ -181,7 +186,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name, true), null, 2.0f, "cantfindcontroller", 30.0f);
|
||||
character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name).Value, delay: 2.0f, identifier: "cantfindcontroller".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
Abandon = true;
|
||||
return;
|
||||
|
||||
+6
-6
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectivePrepare : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "prepare";
|
||||
public override Identifier Identifier { get; set; } = "prepare".ToIdentifier();
|
||||
public override string DebugTag => $"{Identifier}";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool KeepDivingGearOnAlsoWhenInactive => true;
|
||||
@@ -19,8 +19,8 @@ namespace Barotrauma
|
||||
private AIObjectiveGetItems getMultipleItemsObjective;
|
||||
private bool subObjectivesCreated;
|
||||
private readonly Item targetItem;
|
||||
private readonly ImmutableArray<string> requiredItems;
|
||||
private readonly ImmutableArray<string> optionalItems;
|
||||
private readonly ImmutableArray<Identifier> requiredItems;
|
||||
private readonly ImmutableArray<Identifier> optionalItems;
|
||||
private readonly HashSet<Item> items = new HashSet<Item>();
|
||||
public bool KeepActiveWhenReady { get; set; }
|
||||
public bool CheckInventory { get; set; }
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma
|
||||
this.targetItem = targetItem;
|
||||
}
|
||||
|
||||
public AIObjectivePrepare(Character character, AIObjectiveManager objectiveManager, IEnumerable<string> optionalItems, IEnumerable<string> requiredItems = null, float priorityModifier = 1)
|
||||
public AIObjectivePrepare(Character character, AIObjectiveManager objectiveManager, IEnumerable<Identifier> optionalItems, IEnumerable<Identifier> requiredItems = null, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.optionalItems = optionalItems.ToImmutableArray();
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
{
|
||||
getAllItemsObjective = CreateObjectives(requiredItems, requireAll: true);
|
||||
}
|
||||
AIObjectiveGetItems CreateObjectives(IEnumerable<string> itemTags, bool requireAll)
|
||||
AIObjectiveGetItems CreateObjectives(IEnumerable<Identifier> itemTags, bool requireAll)
|
||||
{
|
||||
AIObjectiveGetItems objectiveReference = null;
|
||||
if (!TryAddSubObjective(ref objectiveReference, () => new AIObjectiveGetItems(character, objectiveManager, itemTags)
|
||||
@@ -148,7 +148,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<string> allItems = optionalItems;
|
||||
IEnumerable<Identifier> allItems = optionalItems;
|
||||
if (requiredItems != null && requiredItems.Any())
|
||||
{
|
||||
allItems = requiredItems;
|
||||
|
||||
+4
-3
@@ -9,13 +9,13 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectivePumpWater : AIObjectiveLoop<Pump>
|
||||
{
|
||||
public override string Identifier { get; set; } = "pump water";
|
||||
public override Identifier Identifier { get; set; } = "pump water".ToIdentifier();
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
|
||||
private IEnumerable<Pump> pumpList;
|
||||
|
||||
public AIObjectivePumpWater(Character character, AIObjectiveManager objectiveManager, string option, float priorityModifier = 1)
|
||||
public AIObjectivePumpWater(Character character, AIObjectiveManager objectiveManager, Identifier option, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier, option) { }
|
||||
|
||||
protected override void FindTargets()
|
||||
@@ -41,6 +41,7 @@ namespace Barotrauma
|
||||
if (!character.Submarine.IsConnectedTo(pump.Item.Submarine)) { return false; }
|
||||
}
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == pump.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
|
||||
if (pump.Item.IsClaimedByBallastFlora) { return false; }
|
||||
if (IsReady(pump)) { return false; }
|
||||
return true;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (pumpList == null)
|
||||
{
|
||||
if (character == null || character.Submarine == null) { return new Pump[0]; }
|
||||
if (character == null || character.Submarine == null) { return Array.Empty<Pump>(); }
|
||||
pumpList = character.Submarine.GetItems(true).Select(i => i.GetComponent<Pump>()).Where(p => p != null);
|
||||
}
|
||||
return pumpList;
|
||||
|
||||
+11
-7
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
@@ -9,9 +8,10 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveRepairItem : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "repair item";
|
||||
public override Identifier Identifier { get; set; } = "repair item".ToIdentifier();
|
||||
|
||||
public override bool AllowInAnySub => true;
|
||||
public override bool KeepDivingGearOn => Item?.CurrentHull == null;
|
||||
|
||||
public Item Item { get; private set; }
|
||||
|
||||
@@ -52,6 +52,10 @@ namespace Barotrauma
|
||||
Priority = 0;
|
||||
IsCompleted = true;
|
||||
}
|
||||
else if (Item.IsClaimedByBallastFlora)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float distanceFactor = 1;
|
||||
@@ -70,7 +74,7 @@ namespace Barotrauma
|
||||
float reduction = isPriority ? 1 : isSelected ? 2 : 3;
|
||||
float max = AIObjectiveManager.LowestOrderPriority - reduction;
|
||||
float highestWeight = -1;
|
||||
foreach (string tag in Item.Prefab.Tags)
|
||||
foreach (Identifier tag in Item.Prefab.Tags)
|
||||
{
|
||||
if (JobPrefab.ItemRepairPriorities.TryGetValue(tag, out float weight) && weight > highestWeight)
|
||||
{
|
||||
@@ -92,7 +96,7 @@ namespace Barotrauma
|
||||
IsCompleted = Item.IsFullCondition;
|
||||
if (character.IsOnPlayerTeam && IsCompleted && IsRepairing())
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, true), null, 0.0f, "itemrepaired", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariable("DialogItemRepaired", "[itemname]", Item.Name, FormatCapitals.Yes).Value, null, 0.0f, "itemrepaired".ToIdentifier(), 10.0f);
|
||||
}
|
||||
return IsCompleted;
|
||||
}
|
||||
@@ -118,7 +122,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindrequireditemtorepair"), null, 0.0f, "dialogcannotfindrequireditemtorepair", 10.0f);
|
||||
getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindrequireditemtorepair").Value, null, 0.0f, "dialogcannotfindrequireditemtorepair".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
subObjectives.Add(getItemObjective);
|
||||
@@ -206,7 +210,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.IsOnPlayerTeam && IsRepairing())
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, FormatCapitals.Yes).Value, null, 0.0f, "cannotrepair".ToIdentifier(), 10.0f);
|
||||
}
|
||||
repairable.StopRepairing(character);
|
||||
}
|
||||
@@ -243,7 +247,7 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
if (character.IsOnPlayerTeam && IsRepairing())
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, FormatCapitals.Yes).Value, null, 0.0f, "cannotrepair".ToIdentifier(), 10.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+5
-4
@@ -9,12 +9,12 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveRepairItems : AIObjectiveLoop<Item>
|
||||
{
|
||||
public override string Identifier { get; set; } = "repair items";
|
||||
public override Identifier Identifier { get; set; } = "repair items".ToIdentifier();
|
||||
|
||||
/// <summary>
|
||||
/// If set, only fix items where required skill matches this.
|
||||
/// </summary>
|
||||
public string RelevantSkill;
|
||||
public Identifier RelevantSkill;
|
||||
|
||||
public Item PrioritizedItem { get; private set; }
|
||||
|
||||
@@ -72,9 +72,9 @@ namespace Barotrauma
|
||||
if (NearlyFullCondition(item)) { return false; }
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(RelevantSkill))
|
||||
if (!RelevantSkill.IsEmpty)
|
||||
{
|
||||
if (item.Repairables.None(r => r.requiredSkills.Any(s => s.Identifier.Equals(RelevantSkill, StringComparison.OrdinalIgnoreCase)))) { return false; }
|
||||
if (item.Repairables.None(r => r.requiredSkills.Any(s => s.Identifier == RelevantSkill))) { return false; }
|
||||
}
|
||||
return !HumanAIController.IsItemRepairedByAnother(item, out _);
|
||||
}
|
||||
@@ -151,6 +151,7 @@ namespace Barotrauma
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (item.IsFullCondition) { return false; }
|
||||
if (item.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { return false; }
|
||||
//player crew ignores items in outposts
|
||||
if (character.IsOnPlayerTeam && item.Submarine.Info.IsOutpost) { return false; }
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return false; }
|
||||
|
||||
+46
-40
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveRescue : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "rescue";
|
||||
public override Identifier Identifier { get; set; } = "rescue".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
private float findHullTimer;
|
||||
private bool ignoreOxygen;
|
||||
private readonly float findHullInterval = 1.0f;
|
||||
private bool performedCpr;
|
||||
|
||||
public AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -146,9 +147,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetCharacter.CurrentHull != null && HumanAIController.VisibleHulls.Contains(targetCharacter.CurrentHull) && targetCharacter.CurrentHull.DisplayName != null)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget", new string[2] { "[targetname]", "[roomname]" },
|
||||
new string[2] { targetCharacter.Name, targetCharacter.CurrentHull.DisplayName }, new bool[2] { false, true }),
|
||||
null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f);
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[roomname]", targetCharacter.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundunconscioustarget{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
// Go to the target and select it
|
||||
if (!character.CanInteractWith(targetCharacter))
|
||||
@@ -158,7 +160,7 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
|
||||
{
|
||||
CloseEnough = CloseEnoughToTreat,
|
||||
DialogueIdentifier = "dialogcannotreachpatient",
|
||||
DialogueIdentifier = "dialogcannotreachpatient".ToIdentifier(),
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
@@ -216,15 +218,15 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
|
||||
{
|
||||
CloseEnough = CloseEnoughToTreat,
|
||||
DialogueIdentifier = "dialogcannotreachpatient",
|
||||
DialogueIdentifier = "dialogcannotreachpatient".ToIdentifier(),
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -233,18 +235,19 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetCharacter.CurrentHull?.DisplayName != null)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget", new string[2] { "[targetname]", "[roomname]" },
|
||||
new string[2] { targetCharacter.Name, targetCharacter.CurrentHull.DisplayName }, new bool[2] { false, true }),
|
||||
null, 1.0f, "foundwoundedtarget" + targetCharacter.Name, 60.0f);
|
||||
character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[roomname]", targetCharacter.CurrentHull.DisplayName, FormatCapitals.Yes)).Value,
|
||||
null, 1.0f, $"foundwoundedtarget{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
}
|
||||
GiveTreatment(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<string> suitableItemIdentifiers = new List<string>();
|
||||
private readonly List<string> itemNameList = new List<string>();
|
||||
private readonly Dictionary<string, float> currentTreatmentSuitabilities = new Dictionary<string, float>();
|
||||
private readonly List<Identifier> suitableItemIdentifiers = new List<Identifier>();
|
||||
private readonly List<LocalizedString> itemNameList = new List<LocalizedString>();
|
||||
private readonly Dictionary<Identifier, float> currentTreatmentSuitabilities = new Dictionary<Identifier, float>();
|
||||
private void GiveTreatment(float deltaTime)
|
||||
{
|
||||
if (targetCharacter == null)
|
||||
@@ -281,7 +284,7 @@ namespace Barotrauma
|
||||
if (affliction.Prefab == null) { throw new Exception("Affliction prefab was null"); }
|
||||
float bestSuitability = 0.0f;
|
||||
Item bestItem = null;
|
||||
foreach (KeyValuePair<string, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability)
|
||||
foreach (KeyValuePair<Identifier, float> treatmentSuitability in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
if (currentTreatmentSuitabilities.ContainsKey(treatmentSuitability.Key) &&
|
||||
currentTreatmentSuitabilities[treatmentSuitability.Key] > bestSuitability)
|
||||
@@ -311,12 +314,12 @@ namespace Barotrauma
|
||||
{
|
||||
itemNameList.Clear();
|
||||
suitableItemIdentifiers.Clear();
|
||||
foreach (KeyValuePair<string, float> treatmentSuitability in currentTreatmentSuitabilities)
|
||||
foreach (KeyValuePair<Identifier, float> treatmentSuitability in currentTreatmentSuitabilities)
|
||||
{
|
||||
if (treatmentSuitability.Value <= cprSuitability) { continue; }
|
||||
if (MapEntityPrefab.Find(null, treatmentSuitability.Key, showErrorMessages: false) is ItemPrefab itemPrefab)
|
||||
{
|
||||
if (!Item.ItemList.Any(it => it.prefab.Identifier == treatmentSuitability.Key)) { continue; }
|
||||
if (!Item.ItemList.Any(it => ((MapEntity)it).Prefab.Identifier == treatmentSuitability.Key)) { continue; }
|
||||
suitableItemIdentifiers.Add(treatmentSuitability.Key);
|
||||
//only list the first 4 items
|
||||
if (itemNameList.Count < 4)
|
||||
@@ -327,7 +330,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (itemNameList.Any())
|
||||
{
|
||||
string itemListStr = "";
|
||||
LocalizedString itemListStr = "";
|
||||
if (itemNameList.Count == 1)
|
||||
{
|
||||
itemListStr = itemNameList[0];
|
||||
@@ -337,33 +340,34 @@ namespace Barotrauma
|
||||
//[treatment1] or [treatment2]
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsLast",
|
||||
new string[] { "[treatment1]", "[treatment2]" },
|
||||
new string[] { itemNameList[0], itemNameList[1] });
|
||||
("[treatment1]", itemNameList[0]),
|
||||
("[treatment2]", itemNameList[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
//[treatment1], [treatment2], [treatment3] ... or [treatmentx]
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsFirst",
|
||||
new string[] { "[treatment1]", "[treatment2]" },
|
||||
new string[] { itemNameList[0], itemNameList[1] });
|
||||
("[treatment1]", itemNameList[0]),
|
||||
("[treatment2]", itemNameList[1]));
|
||||
for (int i = 2; i < itemNameList.Count - 1; i++)
|
||||
{
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsFirst",
|
||||
new string[] { "[treatment1]", "[treatment2]" },
|
||||
new string[] { itemListStr, itemNameList[i] });
|
||||
("[treatment1]", itemListStr),
|
||||
("[treatment2]", itemNameList[i]));
|
||||
}
|
||||
itemListStr = TextManager.GetWithVariables(
|
||||
"DialogRequiredTreatmentOptionsLast",
|
||||
new string[] { "[treatment1]", "[treatment2]" },
|
||||
new string[] { itemListStr, itemNameList.Last() });
|
||||
("[treatment1]", itemListStr),
|
||||
("[treatment2]", itemNameList.Last()));
|
||||
}
|
||||
if (targetCharacter != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogListRequiredTreatments", new string[2] { "[targetname]", "[treatmentlist]" },
|
||||
new string[2] { targetCharacter.Name, itemListStr }, new bool[2] { false, true }),
|
||||
null, 2.0f, "listrequiredtreatments" + targetCharacter.Name, 60.0f);
|
||||
character.Speak(TextManager.GetWithVariables("DialogListRequiredTreatments",
|
||||
("[targetname]", targetCharacter.Name, FormatCapitals.No),
|
||||
("[treatmentlist]", itemListStr, FormatCapitals.Yes)).Value,
|
||||
null, 2.0f, $"listrequiredtreatments{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
}
|
||||
RemoveSubObjective(ref getItemObjective);
|
||||
TryAddSubObjective(ref getItemObjective,
|
||||
@@ -374,13 +378,13 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
if (character != targetCharacter && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f);
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, FormatCapitals.No).Value, identifier: "cannottreatpatient".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (cprSuitability <= 0)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f);
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: FormatCapitals.No).Value, identifier: "cannottreatpatient".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
|
||||
Abandon = true;
|
||||
}
|
||||
}
|
||||
@@ -388,7 +392,7 @@ namespace Barotrauma
|
||||
else if (!targetCharacter.IsUnconscious)
|
||||
{
|
||||
//no suitable treatments found, not inside our own sub (= can't search for more treatments), the target isn't unconscious (= can't give CPR)
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: false), identifier: "cannottreatpatient", minDurationBetweenSimilar: 20.0f);
|
||||
character.Speak(TextManager.GetWithVariable("dialogcannottreatpatient", "[name]", targetCharacter.DisplayName, formatCapitals: FormatCapitals.No).Value, identifier: "cannottreatpatient".ToIdentifier(), minDurationBetweenSimilar: 20.0f);
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
@@ -398,6 +402,7 @@ namespace Barotrauma
|
||||
{
|
||||
character.SelectCharacter(targetCharacter);
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
performedCpr = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -425,7 +430,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (remove)
|
||||
{
|
||||
Entity.Spawner?.AddToRemoveQueue(item);
|
||||
Entity.Spawner?.AddItemToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,9 +438,10 @@ namespace Barotrauma
|
||||
{
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter);
|
||||
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name),
|
||||
null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f);
|
||||
{
|
||||
string textTag = performedCpr ? "DialogTargetResuscitated" : "DialogTargetHealed";
|
||||
string message = TextManager.GetWithVariable(textTag, "[targetname]", targetCharacter.Name)?.Value;
|
||||
character.Speak(message, delay: 1.0f, identifier: $"targethealed{targetCharacter.Name}".ToIdentifier(), minDurationBetweenSimilar: 60.0f);
|
||||
}
|
||||
return isCompleted;
|
||||
}
|
||||
|
||||
+5
-2
@@ -7,7 +7,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveRescueAll : AIObjectiveLoop<Character>
|
||||
{
|
||||
public override string Identifier { get; set; } = "rescue all";
|
||||
public override Identifier Identifier { get; set; } = "rescue all".ToIdentifier();
|
||||
public override bool ForceRun => true;
|
||||
public override bool InverseTargetEvaluation => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
@@ -112,12 +112,15 @@ namespace Barotrauma
|
||||
{
|
||||
if (GetVitalityFactor(target) >= vitalityThreshold) { return false; }
|
||||
}
|
||||
if (target.Submarine != character.Submarine) { return false; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
// 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; }
|
||||
}
|
||||
else
|
||||
{
|
||||
return target.Submarine == null;
|
||||
}
|
||||
if (target != character && target.IsBot && HumanAIController.IsActive(target) && target.AIController is HumanAIController targetAI)
|
||||
{
|
||||
// Ignore all concious targets that are currently fighting, fleeing, fixing, or treating characters
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveReturn : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "return";
|
||||
public override Identifier Identifier { get; set; } = "return".ToIdentifier();
|
||||
public Submarine ReturnTarget { get; }
|
||||
|
||||
private AIObjectiveGoTo moveInsideObjective, moveOutsideObjective;
|
||||
@@ -93,7 +93,7 @@ namespace Barotrauma
|
||||
// Target the closest airlock
|
||||
float closestDist = 0;
|
||||
Hull airlock = null;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine != targetHull.Submarine) { continue; }
|
||||
if (!hull.IsTaggedAirlock()) { continue; }
|
||||
@@ -210,10 +210,10 @@ namespace Barotrauma
|
||||
SteeringManager?.Reset();
|
||||
if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective)
|
||||
{
|
||||
string msg = TextManager.Get("dialogcannotreturn", returnNull: true);
|
||||
if (msg != null)
|
||||
string msg = TextManager.Get("dialogcannotreturn").Value;
|
||||
if (!msg.IsNullOrEmpty())
|
||||
{
|
||||
character.Speak(msg, identifier: "dialogcannotreturn", minDurationBetweenSimilar: 5.0f);
|
||||
character.Speak(msg, identifier: "dialogcannotreturn".ToIdentifier(), minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user