(94ad97066) Refactor combat objective. The bots should now be able to reload and seek more ammunition. When they run out of ammunition, they first try to use another weapon and only if no other weapon is found, they seek more ammunition. Had to change the contain item and get item objectives a bit. Hopefully doesn't break things too much.
This commit is contained in:
@@ -106,6 +106,18 @@ namespace Barotrauma
|
||||
subObjectives.Add(objective);
|
||||
}
|
||||
|
||||
public void RemoveSubObjective<T>(ref T objective) where T : AIObjective
|
||||
{
|
||||
if (objective != null)
|
||||
{
|
||||
if (subObjectives.Contains(objective))
|
||||
{
|
||||
subObjectives.Remove(objective);
|
||||
}
|
||||
objective = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SortSubObjectives()
|
||||
{
|
||||
if (subObjectives.None()) { return; }
|
||||
|
||||
@@ -11,9 +11,10 @@ namespace Barotrauma
|
||||
class AIObjectiveCombat : AIObjective
|
||||
{
|
||||
public override string DebugTag => "combat";
|
||||
public bool useCoolDown = true;
|
||||
|
||||
const float CoolDown = 10.0f;
|
||||
private readonly CombatMode initialMode;
|
||||
|
||||
const float coolDown = 10.0f;
|
||||
|
||||
public Character Enemy { get; private set; }
|
||||
|
||||
@@ -25,14 +26,7 @@ namespace Barotrauma
|
||||
{
|
||||
_weapon = value;
|
||||
_weaponComponent = null;
|
||||
if (reloadWeaponObjective != null)
|
||||
{
|
||||
if (subObjectives.Contains(reloadWeaponObjective))
|
||||
{
|
||||
subObjectives.Remove(reloadWeaponObjective);
|
||||
}
|
||||
reloadWeaponObjective = null;
|
||||
}
|
||||
RemoveSubObjective(ref seekAmmunition);
|
||||
}
|
||||
}
|
||||
private ItemComponent _weaponComponent;
|
||||
@@ -40,6 +34,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Weapon == null) { return null; }
|
||||
if (_weaponComponent == null)
|
||||
{
|
||||
_weaponComponent =
|
||||
@@ -58,12 +53,14 @@ namespace Barotrauma
|
||||
private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
|
||||
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
|
||||
|
||||
private AIObjectiveContainItem reloadWeaponObjective;
|
||||
private AIObjectiveContainItem seekAmmunition;
|
||||
private AIObjectiveGoTo retreatObjective;
|
||||
private AIObjectiveGoTo followTargetObjective;
|
||||
|
||||
private Hull retreatTarget;
|
||||
private float coolDownTimer;
|
||||
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
|
||||
private float aimTimer;
|
||||
|
||||
public enum CombatMode
|
||||
{
|
||||
@@ -78,7 +75,7 @@ namespace Barotrauma
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
Enemy = enemy;
|
||||
coolDownTimer = CoolDown;
|
||||
coolDownTimer = coolDown;
|
||||
findSafety = objectiveManager.GetObjective<AIObjectiveFindSafety>();
|
||||
if (findSafety != null)
|
||||
{
|
||||
@@ -86,6 +83,7 @@ namespace Barotrauma
|
||||
findSafety.unreachable.Clear();
|
||||
}
|
||||
Mode = mode;
|
||||
initialMode = Mode;
|
||||
if (Enemy == null)
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
@@ -104,7 +102,7 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (useCoolDown && coolDownTimer <= 0);
|
||||
bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (initialMode != CombatMode.Offensive && coolDownTimer <= 0);
|
||||
if (completed)
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead)
|
||||
@@ -121,65 +119,93 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (useCoolDown)
|
||||
if (initialMode != CombatMode.Offensive)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
}
|
||||
if (abandon) { return; }
|
||||
Arm(deltaTime);
|
||||
Move(deltaTime);
|
||||
TryArm();
|
||||
if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition))
|
||||
{
|
||||
Move();
|
||||
if (Weapon != null)
|
||||
{
|
||||
OperateWeapon(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Arm(float deltaTime)
|
||||
private void Move()
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case CombatMode.Offensive:
|
||||
Engage();
|
||||
break;
|
||||
case CombatMode.Defensive:
|
||||
if (Weapon != null && !character.Inventory.Items.Contains(_weapon) || _weaponComponent != null && !_weaponComponent.HasRequiredContainedItems(false))
|
||||
case CombatMode.Retreat:
|
||||
Retreat();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryArm()
|
||||
{
|
||||
if (Weapon != null)
|
||||
{
|
||||
if (!character.Inventory.Items.Contains(Weapon) || WeaponComponent == null)
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
else if (!WeaponComponent.HasRequiredContainedItems(false))
|
||||
{
|
||||
// Seek ammunition only if cannot find a new weapon
|
||||
if (!Reload(true, () => GetWeapon() == null))
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon();
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
if (Equip())
|
||||
{
|
||||
if (Reload(deltaTime))
|
||||
if (seekAmmunition != null && subObjectives.Contains(seekAmmunition))
|
||||
{
|
||||
Attack(deltaTime);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CombatMode.Retreat:
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon();
|
||||
}
|
||||
if (Weapon == null)
|
||||
{
|
||||
Weapon = GetWeapon(ignoreRequiredItems: true);
|
||||
}
|
||||
Mode = Weapon == null ? CombatMode.Retreat : initialMode;
|
||||
return Weapon != null;
|
||||
}
|
||||
|
||||
private void Move(float deltaTime)
|
||||
private void OperateWeapon(float deltaTime)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case CombatMode.Offensive:
|
||||
Engage(deltaTime);
|
||||
break;
|
||||
case CombatMode.Defensive:
|
||||
if (Equip())
|
||||
{
|
||||
Attack(deltaTime);
|
||||
}
|
||||
break;
|
||||
case CombatMode.Retreat:
|
||||
Retreat(deltaTime);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private Item GetWeapon()
|
||||
private Item GetWeapon(bool ignoreRequiredItems = false)
|
||||
{
|
||||
rangedWeapons.Clear();
|
||||
meleeWeapons.Clear();
|
||||
@@ -193,14 +219,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (component is RangedWeapon rw)
|
||||
{
|
||||
if (rw.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false))
|
||||
{
|
||||
rangedWeapons.Add(rw);
|
||||
}
|
||||
}
|
||||
else if (component is MeleeWeapon mw)
|
||||
{
|
||||
if (mw.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false))
|
||||
{
|
||||
meleeWeapons.Add(mw);
|
||||
}
|
||||
@@ -216,7 +242,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (statusEffect.Afflictions.Any())
|
||||
{
|
||||
if (component.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || component.HasRequiredContainedItems(false))
|
||||
{
|
||||
adHocWeapons.Add(item);
|
||||
}
|
||||
@@ -253,19 +279,29 @@ namespace Barotrauma
|
||||
Weapon.Drop(character);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Equip()
|
||||
{
|
||||
if (!character.SelectedItems.Contains(Weapon))
|
||||
if (!WeaponComponent.HasRequiredContainedItems(false))
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
return false;
|
||||
}
|
||||
//if (!character.SelectedItems.Contains(Weapon))
|
||||
if (!character.HasEquippedItem(Weapon))
|
||||
{
|
||||
Weapon.TryInteract(character, forceSelectKey: true);
|
||||
var slots = Weapon.AllowedSlots.FindAll(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand));
|
||||
if (character.Inventory.TryPutItem(Weapon, character, slots))
|
||||
{
|
||||
Weapon.Equip(character);
|
||||
aimTimer = Rand.Range(0.5f, 1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Weapon = null;
|
||||
Mode = CombatMode.Retreat;
|
||||
return false;
|
||||
}
|
||||
@@ -273,16 +309,10 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Retreat(float deltaTime)
|
||||
private void Retreat()
|
||||
{
|
||||
if (followTargetObjective != null)
|
||||
{
|
||||
if (subObjectives.Contains(followTargetObjective))
|
||||
{
|
||||
subObjectives.Remove(followTargetObjective);
|
||||
}
|
||||
followTargetObjective = null;
|
||||
}
|
||||
RemoveSubObjective(ref followTargetObjective);
|
||||
RemoveSubObjective(ref seekAmmunition);
|
||||
if (retreatObjective != null && retreatObjective.Target != retreatTarget)
|
||||
{
|
||||
retreatObjective = null;
|
||||
@@ -294,59 +324,115 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true));
|
||||
}
|
||||
|
||||
private void Engage(float deltaTime)
|
||||
private void Engage()
|
||||
{
|
||||
retreatTarget = null;
|
||||
if (retreatObjective != null)
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
RemoveSubObjective(ref seekAmmunition);
|
||||
if (followTargetObjective != null && followTargetObjective.Target != Enemy)
|
||||
{
|
||||
if (subObjectives.Contains(retreatObjective))
|
||||
{
|
||||
subObjectives.Remove(retreatObjective);
|
||||
}
|
||||
retreatObjective = null;
|
||||
followTargetObjective = null;
|
||||
}
|
||||
TryAddSubObjective(ref followTargetObjective,
|
||||
constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true)
|
||||
{
|
||||
AllowGoingOutside = true,
|
||||
IgnoreIfTargetDead = true,
|
||||
CheckVisibility = true,
|
||||
CloseEnough =
|
||||
WeaponComponent is RangedWeapon ? 3 :
|
||||
WeaponComponent is MeleeWeapon mw ? ConvertUnits.ToSimUnits(mw.Range) :
|
||||
WeaponComponent is RepairTool rt ? ConvertUnits.ToSimUnits(rt.Range) : 0.5f
|
||||
CheckVisibility = true
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
Mode = CombatMode.Retreat;
|
||||
SteeringManager.Reset();
|
||||
});
|
||||
if (followTargetObjective != null && subObjectives.Contains(followTargetObjective))
|
||||
{
|
||||
followTargetObjective.CloseEnough =
|
||||
WeaponComponent is RangedWeapon ? 3 :
|
||||
WeaponComponent is MeleeWeapon mw ? ConvertUnits.ToSimUnits(mw.Range) :
|
||||
WeaponComponent is RepairTool rt ? ConvertUnits.ToSimUnits(rt.Range) : 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Reload(float deltaTime)
|
||||
/// <summary>
|
||||
/// Seeks for more ammunition. Creates a new subobjective.
|
||||
/// </summary>
|
||||
private void SeekAmmunition(string[] ammunitionIdentifiers)
|
||||
{
|
||||
if (WeaponComponent != null && WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
|
||||
{
|
||||
var containedItems = Weapon.ContainedItems;
|
||||
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
retreatTarget = null;
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
RemoveSubObjective(ref followTargetObjective);
|
||||
TryAddSubObjective(ref seekAmmunition,
|
||||
constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager)
|
||||
{
|
||||
Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
|
||||
if (containedItem == null)
|
||||
targetItemCount = Weapon.GetComponent<ItemContainer>().Capacity,
|
||||
checkInventory = false
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
Weapon = null;
|
||||
Mode = CombatMode.Retreat;
|
||||
SteeringManager.Reset();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the ammunition found in the inventory.
|
||||
/// If seekAmmo is true and the condition is met or not provided, tries to get find the ammo elsewhere.
|
||||
/// </summary>
|
||||
private bool Reload(bool seekAmmo, Func<bool> condition = null)
|
||||
{
|
||||
if (WeaponComponent == null) { return false; }
|
||||
if (!WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) { return false; }
|
||||
var containedItems = Weapon.ContainedItems;
|
||||
RelatedItem item = null;
|
||||
Item ammunition = null;
|
||||
string[] ammunitionIdentifiers = null;
|
||||
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
|
||||
{
|
||||
ammunition = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
|
||||
if (ammunition != null)
|
||||
{
|
||||
// Ammunition still remaining
|
||||
return true;
|
||||
}
|
||||
item = requiredItem;
|
||||
ammunitionIdentifiers = requiredItem.Identifiers;
|
||||
}
|
||||
// No ammo
|
||||
if (ammunition == null)
|
||||
{
|
||||
var container = Weapon.GetComponent<ItemContainer>();
|
||||
// Try reload ammunition in inventory
|
||||
foreach (string identifier in ammunitionIdentifiers)
|
||||
{
|
||||
foreach (var i in character.Inventory.Items)
|
||||
{
|
||||
TryAddSubObjective(ref reloadWeaponObjective,
|
||||
constructor: () => new AIObjectiveContainItem(character, requiredItem.Identifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager),
|
||||
onAbandon: () =>
|
||||
if (i == null) { continue; }
|
||||
if (i.Prefab.Identifier == identifier || i.HasTag(identifier))
|
||||
{
|
||||
if (i.Condition > 0)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
Mode = CombatMode.Retreat;
|
||||
});
|
||||
container.Inventory.TryPutItem(ammunition, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reloadWeaponObjective == null || reloadWeaponObjective.IsCompleted();
|
||||
if (WeaponComponent.HasRequiredContainedItems(false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ammunition == null)
|
||||
{
|
||||
if (seekAmmo && ammunitionIdentifiers != null && (condition == null || condition()))
|
||||
{
|
||||
SeekAmmunition(ammunitionIdentifiers);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
|
||||
private void Attack(float deltaTime)
|
||||
{
|
||||
float squaredDistance = Vector2.DistanceSquared(character.Position, Enemy.Position);
|
||||
@@ -371,6 +457,16 @@ namespace Barotrauma
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
}
|
||||
bool isFacing = character.AnimController.Dir > 0 && Enemy.WorldPosition.X > character.WorldPosition.X || character.AnimController.Dir < 0 && Enemy.WorldPosition.X < character.WorldPosition.X;
|
||||
if (!isFacing)
|
||||
{
|
||||
aimTimer = Rand.Range(1f, 1.5f);
|
||||
}
|
||||
if (aimTimer > 0)
|
||||
{
|
||||
aimTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
if (WeaponComponent is MeleeWeapon meleeWeapon)
|
||||
{
|
||||
if (squaredDistance <= meleeWeapon.Range * meleeWeapon.Range)
|
||||
@@ -385,14 +481,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (squaredDistance > repairTool.Range * repairTool.Range) { return; }
|
||||
}
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - character.Position) < MathHelper.PiOver4)
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4)
|
||||
{
|
||||
if (myBodies == null)
|
||||
{
|
||||
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
|
||||
}
|
||||
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall;
|
||||
var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
|
||||
var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
|
||||
if (pickedBody != null)
|
||||
{
|
||||
Character target = null;
|
||||
@@ -404,10 +500,11 @@ namespace Barotrauma
|
||||
{
|
||||
target = limb.character;
|
||||
}
|
||||
if (target != null && target == Enemy)
|
||||
if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Weapon.Use(deltaTime, character);
|
||||
aimTimer = Rand.Range(0.25f, 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-16
@@ -11,12 +11,13 @@ namespace Barotrauma
|
||||
|
||||
public Func<Item, float> GetItemPriority;
|
||||
|
||||
public int MinContainedAmount = 1;
|
||||
public int targetItemCount = 1;
|
||||
public string[] ignoredContainerIdentifiers;
|
||||
public bool checkInventory = true;
|
||||
|
||||
//can either be a tag or an identifier
|
||||
private readonly string[] itemIdentifiers;
|
||||
private readonly ItemContainer container;
|
||||
public readonly string[] itemIdentifiers;
|
||||
public readonly ItemContainer container;
|
||||
|
||||
private bool isCompleted;
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
@@ -39,7 +40,6 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
if (isCompleted) { return true; }
|
||||
int containedItemCount = 0;
|
||||
foreach (Item item in container.Inventory.Items)
|
||||
{
|
||||
@@ -48,7 +48,7 @@ namespace Barotrauma
|
||||
containedItemCount++;
|
||||
}
|
||||
}
|
||||
return containedItemCount >= MinContainedAmount;
|
||||
return containedItemCount >= targetItemCount;
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
@@ -62,7 +62,6 @@ namespace Barotrauma
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (isCompleted) { return; }
|
||||
//get the item that should be contained
|
||||
Item itemToContain = null;
|
||||
foreach (string identifier in itemIdentifiers)
|
||||
@@ -72,8 +71,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (itemToContain == null)
|
||||
{
|
||||
if (getItemObjective != null && getItemObjective.IsCompleted())
|
||||
{
|
||||
getItemObjective = null;
|
||||
targetItemCount--;
|
||||
}
|
||||
TryAddSubObjective(ref getItemObjective, () =>
|
||||
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager)
|
||||
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, checkInventory: checkInventory)
|
||||
{
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = ignoredContainerIdentifiers
|
||||
@@ -81,14 +85,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
if (container.Item.ParentInventory == character.Inventory)
|
||||
{
|
||||
var containedItems = container.Inventory.Items;
|
||||
//if there's already something in the mask (empty oxygen tank?), drop it
|
||||
var existingItem = containedItems.FirstOrDefault(i => i != null);
|
||||
if (existingItem != null)
|
||||
{
|
||||
existingItem.Drop(character);
|
||||
}
|
||||
{
|
||||
character.Inventory.RemoveItem(itemToContain);
|
||||
container.Inventory.TryPutItem(itemToContain, null);
|
||||
}
|
||||
@@ -102,7 +99,6 @@ namespace Barotrauma
|
||||
}
|
||||
container.Combine(itemToContain);
|
||||
}
|
||||
isCompleted = true;
|
||||
}
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ namespace Barotrauma
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier) { useCoolDown = false };
|
||||
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
|
||||
@@ -225,6 +225,31 @@ namespace Barotrauma
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
||||
#endif
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
|
||||
// TODO: use the collider size/reach?
|
||||
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||
{
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
||||
if (canOperate)
|
||||
{
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
|
||||
}
|
||||
var repairTool = weldingTool.GetComponent<RepairTool>();
|
||||
if (repairTool == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
||||
#endif
|
||||
abandon = true;
|
||||
return;
|
||||
|
||||
@@ -41,10 +41,10 @@ namespace Barotrauma
|
||||
this.targetItem = targetItem;
|
||||
}
|
||||
|
||||
public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1)
|
||||
: this(character, new string[] { itemIdentifier }, objectiveManager, equip, priorityModifier) { }
|
||||
public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1)
|
||||
: this(character, new string[] { itemIdentifier }, objectiveManager, equip, checkInventory, priorityModifier) { }
|
||||
|
||||
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1)
|
||||
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
currSearchIndex = -1;
|
||||
@@ -54,7 +54,10 @@ namespace Barotrauma
|
||||
{
|
||||
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
|
||||
}
|
||||
CheckInventory();
|
||||
if (checkInventory)
|
||||
{
|
||||
CheckInventory();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInventory()
|
||||
@@ -225,8 +228,7 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsDuplicate(AIObjective otherObjective)
|
||||
{
|
||||
AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
|
||||
if (getItem == null) { return false; }
|
||||
if (!(otherObjective is AIObjectiveGetItem getItem)) { return false; }
|
||||
if (getItem.equip != equip) { return false; }
|
||||
if (getItem.itemIdentifiers != null && itemIdentifiers != null)
|
||||
{
|
||||
@@ -246,7 +248,11 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsCompleted()
|
||||
{
|
||||
if (itemIdentifiers != null)
|
||||
if (targetItem != null)
|
||||
{
|
||||
return HasItem(targetItem);
|
||||
}
|
||||
else if (itemIdentifiers != null)
|
||||
{
|
||||
foreach (string itemName in itemIdentifiers)
|
||||
{
|
||||
@@ -258,14 +264,19 @@ namespace Barotrauma
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (targetItem != null)
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasItem(Item item)
|
||||
{
|
||||
bool isEquipped = !equip || character.HasEquippedItem(item);
|
||||
if (character.Inventory.Items.Contains(item) && isEquipped) { return true; }
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
return rootContainer.ParentInventory.Owner == character;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this)
|
||||
{
|
||||
if (randomTimer > 0)
|
||||
{
|
||||
randomTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRandom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsCompleted() => false;
|
||||
public override bool CanBeCompleted => true;
|
||||
|
||||
|
||||
@@ -274,6 +274,10 @@ namespace Barotrauma
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
if (component.AIOperate(deltaTime, character, this))
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+39
-1
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -14,6 +15,7 @@ namespace Barotrauma
|
||||
public Item Item { get; private set; }
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveContainItem refuelObjective;
|
||||
private float previousCondition = -1;
|
||||
private RepairTool repairTool;
|
||||
|
||||
@@ -71,11 +73,46 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
// Only continue when the get item sub objectives have been completed.
|
||||
if (subObjectives.Any(so => so is AIObjectiveGetItem)) { return; }
|
||||
if (subObjectives.Any()) { return; }
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (repairTool != null)
|
||||
{
|
||||
var containedItems = repairTool.Item.ContainedItems;
|
||||
if (containedItems == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("AIObjectiveRepairItem failed - the item \"" + repairTool + "\" has no proper inventory");
|
||||
#endif
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
// Drop empty tanks
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
if (containedItem == null) { continue; }
|
||||
if (containedItem.Condition <= 0.0f)
|
||||
{
|
||||
containedItem.Drop(character);
|
||||
}
|
||||
}
|
||||
RelatedItem item = null;
|
||||
Item fuel = null;
|
||||
foreach (RelatedItem requiredItem in repairTool.requiredItems[RelatedItem.RelationType.Contained])
|
||||
{
|
||||
item = requiredItem;
|
||||
fuel = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
|
||||
if (fuel != null) { break; }
|
||||
}
|
||||
if (fuel == null)
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, item.Identifiers, repairTool.Item.GetComponent<ItemContainer>(), objectiveManager));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item))
|
||||
{
|
||||
if (repairTool != null)
|
||||
@@ -112,6 +149,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveSubObjective(ref refuelObjective);
|
||||
// If cannot reach the item, approach it.
|
||||
TryAddSubObjective(ref goToObjective,
|
||||
constructor: () =>
|
||||
|
||||
Reference in New Issue
Block a user