(f2cce83a3) AI combat improvements: - Bots now find weapons that are inside the equipped containers (tool boxes). - Bots can now use any item as a weapon as long as there's a combat priority defined for the items. - Adjust the combat priorities a bit; the bots now prioritize welding tool and plasma cutter over wrench or toolbox.

This commit is contained in:
Joonas Rikkonen
2019-06-04 16:52:24 +03:00
parent c4584f7763
commit cdc58316ed
2 changed files with 31 additions and 30 deletions
@@ -49,9 +49,7 @@ namespace Barotrauma
public override bool ConcurrentObjectives => true; public override bool ConcurrentObjectives => true;
private readonly AIObjectiveFindSafety findSafety; private readonly AIObjectiveFindSafety findSafety;
private readonly HashSet<RangedWeapon> rangedWeapons = new HashSet<RangedWeapon>(); private readonly HashSet<ItemComponent> weapons = new HashSet<ItemComponent>();
private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
private AIObjectiveContainItem seekAmmunition; private AIObjectiveContainItem seekAmmunition;
private AIObjectiveGoTo retreatObjective; private AIObjectiveGoTo retreatObjective;
@@ -162,7 +160,7 @@ namespace Barotrauma
else if (!WeaponComponent.HasRequiredContainedItems(false)) else if (!WeaponComponent.HasRequiredContainedItems(false))
{ {
// Seek ammunition only if cannot find a new weapon // 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)) if (seekAmmunition != null && subObjectives.Contains(seekAmmunition))
{ {
@@ -177,11 +175,11 @@ namespace Barotrauma
} }
if (Weapon == null) if (Weapon == null)
{ {
Weapon = GetWeapon(); Weapon = GetWeapon(out _weaponComponent);
} }
if (Weapon == null) if (Weapon == null)
{ {
Weapon = GetWeapon(ignoreRequiredItems: true); Weapon = GetWeapon(out _weaponComponent, ignoreRequiredItems: true);
} }
Mode = Weapon == null ? CombatMode.Retreat : initialMode; Mode = Weapon == null ? CombatMode.Retreat : initialMode;
return Weapon != null; 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(); weapons.Clear();
meleeWeapons.Clear();
adHocWeapons.Clear();
Item weapon = null;
_weaponComponent = null; _weaponComponent = null;
foreach (var item in character.Inventory.Items) foreach (var item in character.Inventory.Items)
{ {
if (item == null) { continue; } 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) foreach (var component in item.Components)
{ {
if (component is RangedWeapon rw) if (component is RangedWeapon rw)
{ {
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false)) if (ignoreRequiredItems || rw.HasRequiredContainedItems(false))
{ {
rangedWeapons.Add(rw); weapons.Add(rw);
} }
} }
else if (component is MeleeWeapon mw) else if (component is MeleeWeapon mw)
{ {
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false)) if (ignoreRequiredItems || mw.HasRequiredContainedItems(false))
{ {
meleeWeapons.Add(mw); weapons.Add(mw);
} }
} }
else else
@@ -244,7 +253,7 @@ namespace Barotrauma
{ {
if (ignoreRequiredItems || component.HasRequiredContainedItems(false)) 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() private void Unequip()
@@ -208,7 +208,13 @@ namespace Barotrauma.Items.Components
get; get;
private set; private set;
} }
/// <summary>
/// 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).
/// </summary>
[Serialize(0f, false)]
public float CombatPriority { get; private set; }
public ItemComponent(Item item, XElement element) public ItemComponent(Item item, XElement element)
{ {
this.item = item; this.item = item;
@@ -539,6 +545,7 @@ namespace Barotrauma.Items.Components
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return 0.0f; return 0.0f;
} }
float average = skillSuccessSum / requiredSkills.Count;
float skillSuccessSum = 0.0f; float skillSuccessSum = 0.0f;
for (int i = 0; i < requiredSkills.Count; i++) for (int i = 0; i < requiredSkills.Count; i++)