Crew AI fixes:
- Characters can use oxygenite shards (or any other item with an "oxygensource" tag) in diving suits/masks. - AIObjectiveFindDivingGear doesn't count as being completed if the character has diving gear in their inventory unless the character has equipped the gear. - AIObjectiveGetItem doesn't count as being completed if equipping the target item is required and the character hasn't equipped it yet.
This commit is contained in:
@@ -91,9 +91,8 @@
|
|||||||
|
|
||||||
<Item
|
<Item
|
||||||
name="Oxygenite Shard"
|
name="Oxygenite Shard"
|
||||||
category="Alien"
|
category="Alien"
|
||||||
|
tags="alien,smallitem,oxygensource"
|
||||||
tags="alien,smallitem"
|
|
||||||
impacttolerance="8">
|
impacttolerance="8">
|
||||||
|
|
||||||
<Sprite texture="artifact.png" depth="0.7" sourcerect="119,0,9,32"/>
|
<Sprite texture="artifact.png" depth="0.7" sourcerect="119,0,9,32"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<Item
|
<Item
|
||||||
name="Oxygen Tank"
|
name="Oxygen Tank"
|
||||||
category="Equipment,Misc"
|
category="Equipment,Misc"
|
||||||
Tags="smallitem"
|
Tags="smallitem,oxygensource"
|
||||||
|
|
||||||
price="50">
|
price="50">
|
||||||
|
|
||||||
|
|||||||
+19
-7
@@ -6,24 +6,29 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
class AIObjectiveContainItem: AIObjective
|
class AIObjectiveContainItem: AIObjective
|
||||||
{
|
{
|
||||||
private string itemName;
|
private string[] itemNames;
|
||||||
|
|
||||||
private ItemContainer container;
|
private ItemContainer container;
|
||||||
|
|
||||||
bool isCompleted;
|
bool isCompleted;
|
||||||
|
|
||||||
public bool IgnoreAlreadyContainedItems;
|
public bool IgnoreAlreadyContainedItems;
|
||||||
|
|
||||||
public AIObjectiveContainItem(Character character, string itemName, ItemContainer container)
|
public AIObjectiveContainItem(Character character, string itemName, ItemContainer container)
|
||||||
|
: this(character, new string[] { itemName }, container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AIObjectiveContainItem(Character character, string[] itemNames, ItemContainer container)
|
||||||
: base (character, "")
|
: base (character, "")
|
||||||
{
|
{
|
||||||
this.itemName = itemName;
|
this.itemNames = itemNames;
|
||||||
this.container = container;
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
return isCompleted || container.Inventory.FindItem(itemName)!=null;
|
return isCompleted || itemNames.Any(name => container.Inventory.FindItem(name) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override float GetPriority(AIObjectiveManager objectiveManager)
|
public override float GetPriority(AIObjectiveManager objectiveManager)
|
||||||
@@ -41,10 +46,10 @@ namespace Barotrauma
|
|||||||
if (isCompleted) return;
|
if (isCompleted) return;
|
||||||
|
|
||||||
//get the item that should be contained
|
//get the item that should be contained
|
||||||
var itemToContain = character.Inventory.FindItem(itemName);
|
var itemToContain = character.Inventory.FindItem(itemNames);
|
||||||
if (itemToContain == null)
|
if (itemToContain == null)
|
||||||
{
|
{
|
||||||
var getItem = new AIObjectiveGetItem(character, itemName);
|
var getItem = new AIObjectiveGetItem(character, itemNames);
|
||||||
getItem.IgnoreContainedItems = IgnoreAlreadyContainedItems;
|
getItem.IgnoreContainedItems = IgnoreAlreadyContainedItems;
|
||||||
AddSubObjective(getItem);
|
AddSubObjective(getItem);
|
||||||
return;
|
return;
|
||||||
@@ -79,8 +84,15 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
AIObjectiveContainItem objective = otherObjective as AIObjectiveContainItem;
|
AIObjectiveContainItem objective = otherObjective as AIObjectiveContainItem;
|
||||||
if (objective == null) return false;
|
if (objective == null) return false;
|
||||||
|
if (objective.container != container) return false;
|
||||||
|
if (objective.itemNames.Length != itemNames.Length) return false;
|
||||||
|
|
||||||
return objective.itemName == itemName && objective.container == container;
|
for (int i = 0; i < itemNames.Length; i++)
|
||||||
|
{
|
||||||
|
if (objective.itemNames[i] != itemNames[i]) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+24
-17
@@ -11,12 +11,20 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
var item = character.Inventory.FindItem(gearName);
|
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||||
if (item == null) return false;
|
{
|
||||||
|
if (CharacterInventory.limbSlots[i] == InvSlotType.Any || character.Inventory.Items[i] == null) continue;
|
||||||
|
if (character.Inventory.Items[i].Prefab.NameMatches(gearName) || character.Inventory.Items[i].HasTag(gearName))
|
||||||
|
{
|
||||||
|
var containedItems = character.Inventory.Items[i].ContainedItems;
|
||||||
|
if (containedItems == null) continue;
|
||||||
|
|
||||||
var containedItems = item.ContainedItems;
|
var oxygenTank = Array.Find(containedItems, it => (it.Prefab.NameMatches("Oxygen Tank") || it.HasTag("oxygensource")) && it.Condition > 0.0f);
|
||||||
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank") && i.Condition > 0.0f);
|
if (oxygenTank != null) return true;
|
||||||
return oxygenTank != null;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit)
|
public AIObjectiveFindDivingGear(Character character, bool needDivingSuit)
|
||||||
@@ -41,25 +49,24 @@ namespace Barotrauma
|
|||||||
var containedItems = item.ContainedItems;
|
var containedItems = item.ContainedItems;
|
||||||
if (containedItems == null) return;
|
if (containedItems == null) return;
|
||||||
|
|
||||||
//check if there's an oxygen tank in the mask
|
//check if there's an oxygen tank in the mask/suit
|
||||||
var oxygenTank = Array.Find(containedItems, i => i.Prefab.NameMatches("Oxygen Tank"));
|
foreach (Item containedItem in containedItems)
|
||||||
|
|
||||||
if (oxygenTank != null)
|
|
||||||
{
|
{
|
||||||
if (oxygenTank.Condition > 0.0f)
|
if (containedItem == null) continue;
|
||||||
|
if (containedItem.Condition <= 0.0f)
|
||||||
{
|
{
|
||||||
|
containedItem.Drop();
|
||||||
|
}
|
||||||
|
else if (containedItem.Prefab.NameMatches("Oxygen Tank") || containedItem.HasTag("oxygensource"))
|
||||||
|
{
|
||||||
|
//we've got an oxygen source inside the mask/suit, all good
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
oxygenTank.Drop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
|
if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted())
|
||||||
{
|
{
|
||||||
subObjective = new AIObjectiveContainItem(character, "Oxygen Tank", item.GetComponent<ItemContainer>());
|
subObjective = new AIObjectiveContainItem(character, new string[] { "Oxygen Tank", "oxygensource" }, item.GetComponent<ItemContainer>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
class AIObjectiveGetItem : AIObjective
|
class AIObjectiveGetItem : AIObjective
|
||||||
{
|
{
|
||||||
private string itemName;
|
private string[] itemNames;
|
||||||
|
|
||||||
private Item targetItem, moveToTarget;
|
private Item targetItem, moveToTarget;
|
||||||
|
|
||||||
@@ -49,24 +48,33 @@ namespace Barotrauma
|
|||||||
this.targetItem = targetItem;
|
this.targetItem = targetItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AIObjectiveGetItem(Character character, string itemName, bool equip=false)
|
public AIObjectiveGetItem(Character character, string itemName, bool equip = false)
|
||||||
: base (character, "")
|
: this(character, new string[] { itemName }, equip)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AIObjectiveGetItem(Character character, string[] itemNames, bool equip = false)
|
||||||
|
: base(character, "")
|
||||||
{
|
{
|
||||||
canBeCompleted = true;
|
canBeCompleted = true;
|
||||||
|
|
||||||
this.equip = equip;
|
this.equip = equip;
|
||||||
|
|
||||||
currSearchIndex = 0;
|
currSearchIndex = 0;
|
||||||
|
|
||||||
this.itemName = itemName;
|
this.itemNames = itemNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Act(float deltaTime)
|
protected override void Act(float deltaTime)
|
||||||
{
|
{
|
||||||
FindTargetItem();
|
FindTargetItem();
|
||||||
if (targetItem == null || moveToTarget == null) return;
|
if (targetItem == null || moveToTarget == null)
|
||||||
|
{
|
||||||
|
character?.AIController?.SteeringManager?.Reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance*2.0f)
|
if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance * 2.0f)
|
||||||
{
|
{
|
||||||
int targetSlot = -1;
|
int targetSlot = -1;
|
||||||
if (equip)
|
if (equip)
|
||||||
@@ -82,8 +90,8 @@ namespace Barotrauma
|
|||||||
foreach (InvSlotType slots in pickable.AllowedSlots)
|
foreach (InvSlotType slots in pickable.AllowedSlots)
|
||||||
{
|
{
|
||||||
if (slots.HasFlag(InvSlotType.Any)) continue;
|
if (slots.HasFlag(InvSlotType.Any)) continue;
|
||||||
|
|
||||||
for (int i = 0; i<character.Inventory.Items.Length; i++)
|
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||||
{
|
{
|
||||||
//slot not needed by the item, continue
|
//slot not needed by the item, continue
|
||||||
if (!slots.HasFlag(CharacterInventory.limbSlots[i])) continue;
|
if (!slots.HasFlag(CharacterInventory.limbSlots[i])) continue;
|
||||||
@@ -111,15 +119,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (goToObjective == null)
|
if (goToObjective == null || moveToTarget != goToObjective.Target)
|
||||||
{
|
{
|
||||||
bool gettingDivingGear = itemName == "diving" || itemName == "Diving Gear";
|
//check if we're already looking for a diving gear
|
||||||
|
bool gettingDivingGear = (targetItem != null && targetItem.Prefab.NameMatches("Diving Gear") || targetItem.HasTag("diving")) ||
|
||||||
|
(itemNames != null && (itemNames.Contains("diving") || itemNames.Contains("Diving Gear")));
|
||||||
|
|
||||||
|
//don't attempt to get diving gear to reach the destination if the item we're trying to get is diving gear
|
||||||
goToObjective = new AIObjectiveGoTo(moveToTarget, character, false, !gettingDivingGear);
|
goToObjective = new AIObjectiveGoTo(moveToTarget, character, false, !gettingDivingGear);
|
||||||
}
|
}
|
||||||
|
|
||||||
goToObjective.TryComplete(deltaTime);
|
goToObjective.TryComplete(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -127,14 +139,14 @@ namespace Barotrauma
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void FindTargetItem()
|
private void FindTargetItem()
|
||||||
{
|
{
|
||||||
if (itemName == null)
|
if (itemNames == null)
|
||||||
{
|
{
|
||||||
if (targetItem == null) canBeCompleted = false;
|
if (targetItem == null) canBeCompleted = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position);
|
float currDist = moveToTarget == null ? 0.0f : Vector2.DistanceSquared(moveToTarget.Position, character.Position);
|
||||||
|
|
||||||
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 2; i++)
|
for (int i = 0; i < 10 && currSearchIndex < Item.ItemList.Count - 2; i++)
|
||||||
{
|
{
|
||||||
currSearchIndex++;
|
currSearchIndex++;
|
||||||
@@ -143,21 +155,26 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
|
if (item.CurrentHull == null || item.Condition <= 0.0f) continue;
|
||||||
if (IgnoreContainedItems && item.Container != null) continue;
|
if (IgnoreContainedItems && item.Container != null) continue;
|
||||||
if (item.Name != itemName && !item.HasTag(itemName)) continue;
|
if (!itemNames.Any(name => item.Prefab.NameMatches(name) || item.HasTag(name))) continue;
|
||||||
|
|
||||||
//if the item is inside a character's inventory, don't steal it
|
//if the item is inside a character's inventory, don't steal it unless the character is dead
|
||||||
if (item.ParentInventory is CharacterInventory) continue;
|
if (item.ParentInventory is CharacterInventory)
|
||||||
|
{
|
||||||
|
Character owner = item.ParentInventory.Owner as Character;
|
||||||
|
if (owner != null && !owner.IsDead) continue;
|
||||||
|
}
|
||||||
|
|
||||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||||
if (item.ParentInventory != null && item.ParentInventory.Owner is Item)
|
Item rootContainer = item.GetRootContainer();
|
||||||
|
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||||
{
|
{
|
||||||
if (((Item)item.ParentInventory.Owner).ParentInventory is CharacterInventory) continue;
|
Character owner = rootContainer.ParentInventory.Owner as Character;
|
||||||
|
if (owner != null && !owner.IsDead) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ignore if item is further away than the currently targeted item
|
//ignore if item is further away than the currently targeted item
|
||||||
Item rootContainer = item.GetRootContainer();
|
|
||||||
if (moveToTarget != null && Vector2.DistanceSquared((rootContainer ?? item).Position, character.Position) > currDist) continue;
|
if (moveToTarget != null && Vector2.DistanceSquared((rootContainer ?? item).Position, character.Position) > currDist) continue;
|
||||||
|
|
||||||
targetItem = item;
|
targetItem = item;
|
||||||
moveToTarget = rootContainer ?? item;
|
moveToTarget = rootContainer ?? item;
|
||||||
}
|
}
|
||||||
@@ -165,23 +182,44 @@ namespace Barotrauma
|
|||||||
//if searched through all the items and a target wasn't found, can't be completed
|
//if searched through all the items and a target wasn't found, can't be completed
|
||||||
if (currSearchIndex >= Item.ItemList.Count && targetItem == null) canBeCompleted = false;
|
if (currSearchIndex >= Item.ItemList.Count && targetItem == null) canBeCompleted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsDuplicate(AIObjective otherObjective)
|
public override bool IsDuplicate(AIObjective otherObjective)
|
||||||
{
|
{
|
||||||
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
||||||
if (getItem == null) return false;
|
if (getItem == null) return false;
|
||||||
return (getItem.itemName == itemName);
|
if (getItem.equip != equip) return false;
|
||||||
|
if (getItem.itemNames != null && itemNames != null)
|
||||||
|
{
|
||||||
|
if (getItem.itemNames.Length != itemNames.Length) return false;
|
||||||
|
for (int i = 0; i < getItem.itemNames.Length; i++)
|
||||||
|
{
|
||||||
|
if (getItem.itemNames[i] != itemNames[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (getItem.itemNames == null && itemNames == null)
|
||||||
|
{
|
||||||
|
return getItem.targetItem == targetItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsCompleted()
|
public override bool IsCompleted()
|
||||||
{
|
{
|
||||||
if (itemName!=null)
|
if (itemNames != null)
|
||||||
{
|
{
|
||||||
return character.Inventory.FindItem(itemName) != null;
|
foreach (string itemName in itemNames)
|
||||||
|
{
|
||||||
|
var matchingItem = character.Inventory.FindItem(itemName);
|
||||||
|
if (matchingItem != null && (!equip || character.HasEquippedItem(matchingItem))) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (targetItem!= null)
|
else if (targetItem != null)
|
||||||
{
|
{
|
||||||
return character.Inventory.Items.Contains(targetItem);
|
return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1045,6 +1045,17 @@ namespace Barotrauma
|
|||||||
return !inventory.IsInLimbSlot(item, InvSlotType.Any);
|
return !inventory.IsInLimbSlot(item, InvSlotType.Any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasEquippedItem(string itemName)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < inventory.Items.Length; i++)
|
||||||
|
{
|
||||||
|
if (CharacterInventory.limbSlots[i] == InvSlotType.Any || inventory.Items[i] == null) continue;
|
||||||
|
if (inventory.Items[i].Prefab.NameMatches(itemName) || inventory.Items[i].HasTag(itemName)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasSelectedItem(Item item)
|
public bool HasSelectedItem(Item item)
|
||||||
{
|
{
|
||||||
return selectedItems.Contains(item);
|
return selectedItems.Contains(item);
|
||||||
|
|||||||
@@ -143,10 +143,21 @@ namespace Barotrauma
|
|||||||
public Item FindItem(string itemName)
|
public Item FindItem(string itemName)
|
||||||
{
|
{
|
||||||
if (itemName == null) return null;
|
if (itemName == null) return null;
|
||||||
|
|
||||||
return Items.FirstOrDefault(i => i != null && (i.Prefab.NameMatches(itemName) || i.HasTag(itemName)));
|
return Items.FirstOrDefault(i => i != null && (i.Prefab.NameMatches(itemName) || i.HasTag(itemName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Item FindItem(string[] itemNames)
|
||||||
|
{
|
||||||
|
if (itemNames == null) return null;
|
||||||
|
|
||||||
|
foreach (string itemName in itemNames)
|
||||||
|
{
|
||||||
|
var item = FindItem(itemName);
|
||||||
|
if (item != null) return item;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void RemoveItem(Item item)
|
public virtual void RemoveItem(Item item)
|
||||||
{
|
{
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user