diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 89e3d1e05..2daca7e13 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -49,9 +49,7 @@ namespace Barotrauma public override bool ConcurrentObjectives => true; private readonly AIObjectiveFindSafety findSafety; - private readonly HashSet rangedWeapons = new HashSet(); - private readonly HashSet meleeWeapons = new HashSet(); - private readonly HashSet adHocWeapons = new HashSet(); + private readonly HashSet weapons = new HashSet(); private AIObjectiveContainItem seekAmmunition; private AIObjectiveGoTo retreatObjective; @@ -162,7 +160,7 @@ namespace Barotrauma else if (!WeaponComponent.HasRequiredContainedItems(false)) { // Seek ammunition only if cannot find a new weapon - if (!Reload(true, () => GetWeapon() == null)) + if (!Reload(true, () => GetWeapon(out _) == null)) { if (seekAmmunition != null && subObjectives.Contains(seekAmmunition)) { @@ -177,11 +175,11 @@ namespace Barotrauma } if (Weapon == null) { - Weapon = GetWeapon(); + Weapon = GetWeapon(out _weaponComponent); } if (Weapon == null) { - Weapon = GetWeapon(ignoreRequiredItems: true); + Weapon = GetWeapon(out _weaponComponent, ignoreRequiredItems: true); } Mode = Weapon == null ? CombatMode.Retreat : initialMode; return Weapon != null; @@ -205,30 +203,41 @@ namespace Barotrauma } } - private Item GetWeapon(bool ignoreRequiredItems = false) + private Item GetWeapon(out ItemComponent weaponComponent, bool ignoreRequiredItems = false) { - rangedWeapons.Clear(); - meleeWeapons.Clear(); - adHocWeapons.Clear(); - Item weapon = null; + weapons.Clear(); _weaponComponent = null; foreach (var item in character.Inventory.Items) { if (item == null) { continue; } + SeekWeapons(item); + if (item.OwnInventory != null) + { + item.OwnInventory.Items.ForEach(i => SeekWeapons(i)); + } + } + weaponComponent = weapons.OrderByDescending(w => w.CombatPriority).FirstOrDefault(); + if (weaponComponent == null) { return null; } + if (weaponComponent.CombatPriority < 1) { return null; } + return weaponComponent.Item; + + void SeekWeapons(Item item) + { + if (item == null) { return; } foreach (var component in item.Components) { if (component is RangedWeapon rw) { if (ignoreRequiredItems || rw.HasRequiredContainedItems(false)) { - rangedWeapons.Add(rw); + weapons.Add(rw); } } else if (component is MeleeWeapon mw) { if (ignoreRequiredItems || mw.HasRequiredContainedItems(false)) { - meleeWeapons.Add(mw); + weapons.Add(mw); } } else @@ -244,7 +253,7 @@ namespace Barotrauma { if (ignoreRequiredItems || component.HasRequiredContainedItems(false)) { - adHocWeapons.Add(item); + weapons.Add(component); } } } @@ -253,21 +262,6 @@ namespace Barotrauma } } } - var rangedWeapon = rangedWeapons.OrderByDescending(w => w.CombatPriority).FirstOrDefault(); - var meleeWeapon = meleeWeapons.OrderByDescending(w => w.CombatPriority).FirstOrDefault(); - if (rangedWeapon != null) - { - weapon = rangedWeapon.Item; - } - else if (meleeWeapon != null) - { - weapon = meleeWeapon.Item; - } - if (weapon == null) - { - weapon = adHocWeapons.GetRandom(Rand.RandSync.Server); - } - return weapon; } private void Unequip() diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs index 76daa7efe..bd6d56c53 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs @@ -208,7 +208,13 @@ namespace Barotrauma.Items.Components get; private set; } - + + /// + /// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced). + /// + [Serialize(0f, false)] + public float CombatPriority { get; private set; } + public ItemComponent(Item item, XElement element) { this.item = item; @@ -539,6 +545,7 @@ namespace Barotrauma.Items.Components GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return 0.0f; } + float average = skillSuccessSum / requiredSkills.Count; float skillSuccessSum = 0.0f; for (int i = 0; i < requiredSkills.Count; i++)