Unstable v0.10.600.0
This commit is contained in:
@@ -15,6 +15,7 @@ namespace Barotrauma
|
||||
public virtual bool IgnoreUnsafeHulls => false;
|
||||
public virtual bool AbandonWhenCannotCompleteSubjectives => true;
|
||||
public virtual bool AllowSubObjectiveSorting => false;
|
||||
public virtual bool ForceOrderPriority => true;
|
||||
|
||||
/// <summary>
|
||||
/// Can there be multiple objective instaces of the same type?
|
||||
@@ -34,6 +35,7 @@ namespace Barotrauma
|
||||
public virtual bool AllowAutomaticItemUnequipping => false;
|
||||
public virtual bool AllowOutsideSubmarine => false;
|
||||
public virtual bool AllowInFriendlySubs => false;
|
||||
public virtual bool AllowInAnySub => false;
|
||||
|
||||
protected readonly List<AIObjective> subObjectives = new List<AIObjective>();
|
||||
private float _cumulatedDevotion;
|
||||
@@ -198,12 +200,10 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AllowOutsideSubmarine) { return true; }
|
||||
if (character.Submarine == null) { return false; }
|
||||
return
|
||||
character.Submarine.TeamID == character.TeamID ||
|
||||
(AllowInFriendlySubs && character.Submarine.TeamID == Character.TeamType.FriendlyNPC) ||
|
||||
character.Submarine.DockedTo.Any(sub => sub.TeamID == character.TeamID);
|
||||
if (!AllowOutsideSubmarine && character.Submarine == null) { return false; }
|
||||
if (AllowInAnySub) { return true; }
|
||||
if (AllowInFriendlySubs && character.Submarine.TeamID == Character.TeamType.FriendlyNPC) { return true; }
|
||||
return character.Submarine.TeamID == character.TeamID || character.Submarine.DockedTo.Any(sub => sub.TeamID == character.TeamID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,12 +212,14 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public virtual float GetPriority()
|
||||
{
|
||||
bool isOrder = objectiveManager.CurrentOrder == this;
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
return Priority;
|
||||
}
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
if (isOrder)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
}
|
||||
@@ -306,7 +308,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Attempted to add a duplicate subobjective!\n" + Environment.StackTrace);
|
||||
DebugConsole.ThrowError("Attempted to add a duplicate subobjective!\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCleanupItem : AIObjective
|
||||
{
|
||||
public override string DebugTag => "cleanup item";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowAutomaticItemUnequipping => false;
|
||||
|
||||
public readonly Item item;
|
||||
public bool IsPriority { get; set; }
|
||||
|
||||
private readonly List<Item> ignoredContainers = new List<Item>();
|
||||
private AIObjectiveDecontainItem decontainObjective;
|
||||
private int itemIndex = 0;
|
||||
|
||||
public AIObjectiveCleanupItem(Item item, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
float distanceFactor = 0.9f;
|
||||
if (!IsPriority && item.CurrentHull != character.CurrentHull)
|
||||
{
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - item.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - item.WorldPosition.X) + yDist;
|
||||
distanceFactor = MathHelper.Lerp(0.9f, 0, MathUtils.InverseLerp(0, 5000, dist));
|
||||
}
|
||||
bool isSelected = character.HasItem(item);
|
||||
float selectedBonus = isSelected ? 100 - MaxDevotion : 0;
|
||||
float devotion = (CumulatedDevotion + selectedBonus) / 100;
|
||||
float reduction = IsPriority ? 1 : isSelected ? 2 : 3;
|
||||
float max = MathHelper.Min(AIObjectiveManager.OrderPriority - reduction, 90);
|
||||
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
itemIndex = 0;
|
||||
if (suitableContainer != null)
|
||||
{
|
||||
bool equip = item.HasTag(AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR) || (
|
||||
item.GetComponent<Wearable>() == null &&
|
||||
item.AllowedSlots.None(s =>
|
||||
s == InvSlotType.Card ||
|
||||
s == InvSlotType.Head ||
|
||||
s == InvSlotType.Headset ||
|
||||
s == InvSlotType.InnerClothes ||
|
||||
s == InvSlotType.OuterClothes));
|
||||
TryAddSubObjective(ref decontainObjective, () => new AIObjectiveDecontainItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent<ItemContainer>())
|
||||
{
|
||||
Equip = equip,
|
||||
DropIfFails = true
|
||||
},
|
||||
onCompleted: () =>
|
||||
{
|
||||
if (equip)
|
||||
{
|
||||
HumanAIController.ReequipUnequipped();
|
||||
}
|
||||
IsCompleted = true;
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
if (equip)
|
||||
{
|
||||
HumanAIController.ReequipUnequipped();
|
||||
}
|
||||
if (decontainObjective != null && decontainObjective.ContainObjective != null && decontainObjective.ContainObjective.CanBeCompleted)
|
||||
{
|
||||
ignoredContainers.Add(suitableContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool Check() => IsCompleted;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
ignoredContainers.Clear();
|
||||
itemIndex = 0;
|
||||
decontainObjective = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveCleanupItems : AIObjectiveLoop<Item>
|
||||
{
|
||||
public override string DebugTag => "cleanup items";
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowAutomaticItemUnequipping => false;
|
||||
public override bool ForceOrderPriority => false;
|
||||
|
||||
public readonly Item prioritizedItem;
|
||||
|
||||
public AIObjectiveCleanupItems(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1, Item prioritizedItem = null)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.prioritizedItem = prioritizedItem;
|
||||
}
|
||||
|
||||
protected override float TargetEvaluation() => Targets.Any() ? AIObjectiveManager.RunPriority - 1 : 0;
|
||||
|
||||
protected override bool Filter(Item target)
|
||||
{
|
||||
// If the target was selected as a valid target, we'll have to accept it so that the objective can be completed.
|
||||
// The validity changes when a character picks the item up.
|
||||
if (!IsValidTarget(target, character)) { return Objectives.ContainsKey(target) && IsItemInsideValidSubmarine(target, character); }
|
||||
if (target.CurrentHull.FireSources.Count > 0) { return false; }
|
||||
// Don't repair items in rooms that have enemies inside.
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Item> GetList() => Item.ItemList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Item item)
|
||||
=> new AIObjectiveCleanupItem(item, character, objectiveManager, priorityModifier: PriorityModifier)
|
||||
{
|
||||
IsPriority = prioritizedItem == item
|
||||
};
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Item target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveCleanupItems, Item>(character, target);
|
||||
|
||||
private static bool IsItemInsideValidSubmarine(Item item, Character character)
|
||||
{
|
||||
if (item.CurrentHull == null) { return false; }
|
||||
if (item.Submarine == null) { return false; }
|
||||
if (item.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsValidTarget(Item item, Character character)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (item.NonInteractable) { return false; }
|
||||
if (item.ParentInventory != null) { return false; }
|
||||
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||
//var rootContainer = item.GetRootContainer();
|
||||
//// Only target items lying on the ground (= not inside a container) (do we need this check?)
|
||||
//if (rootContainer != null) { return false; }
|
||||
var pickable = item.GetComponent<Pickable>();
|
||||
if (pickable == null) { return false; }
|
||||
var wire = item.GetComponent<Wire>();
|
||||
if (wire != null)
|
||||
{
|
||||
if (wire.Connections.Any()) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && connectionPanel.Connections.Any(c => c.Wires.Any(w => w != null)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return item.Prefab.PreferredContainers.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-9
@@ -15,6 +15,7 @@ namespace Barotrauma
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
private readonly CombatMode initialMode;
|
||||
|
||||
@@ -54,8 +55,8 @@ namespace Barotrauma
|
||||
if (_weaponComponent == null)
|
||||
{
|
||||
_weaponComponent =
|
||||
Weapon.GetComponent<RangedWeapon>() as ItemComponent ??
|
||||
Weapon.GetComponent<MeleeWeapon>() as ItemComponent ??
|
||||
Weapon.GetComponent<RangedWeapon>() ??
|
||||
Weapon.GetComponent<MeleeWeapon>() ??
|
||||
Weapon.GetComponent<RepairTool>() as ItemComponent;
|
||||
}
|
||||
return _weaponComponent;
|
||||
@@ -144,6 +145,7 @@ namespace Barotrauma
|
||||
if (Enemy.Submarine == null || (Enemy.Submarine.TeamID != character.TeamID && Enemy.Submarine != character.Submarine))
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
}
|
||||
@@ -760,11 +762,7 @@ namespace Barotrauma
|
||||
if (HumanAIController.HasItem(character, "handlocker", out IEnumerable<Item> matchingItems) && Enemy.Stun > 0 && character.CanInteractWith(Enemy))
|
||||
{
|
||||
var handCuffs = matchingItems.First();
|
||||
if (HumanAIController.TryToMoveItem(handCuffs, Enemy.Inventory))
|
||||
{
|
||||
handCuffs.Equip(Enemy);
|
||||
}
|
||||
else
|
||||
if (!HumanAIController.TakeItem(handCuffs, Enemy.Inventory, equip: true))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red);
|
||||
@@ -777,7 +775,7 @@ namespace Barotrauma
|
||||
if (item.StolenDuringRound)
|
||||
{
|
||||
item.Drop(character);
|
||||
character.Inventory.TryPutItem(item, character, new List<InvSlotType>() { InvSlotType.Any });
|
||||
character.Inventory.TryPutItem(item, character, CharacterInventory.anySlot);
|
||||
}
|
||||
}
|
||||
character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
|
||||
@@ -885,7 +883,11 @@ namespace Barotrauma
|
||||
|
||||
private void Attack(float deltaTime)
|
||||
{
|
||||
character.CursorPosition = Enemy.Position;
|
||||
character.CursorPosition = Enemy.WorldPosition;
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
character.CursorPosition -= character.Submarine.Position;
|
||||
}
|
||||
visibilityCheckTimer -= deltaTime;
|
||||
if (visibilityCheckTimer <= 0.0f)
|
||||
{
|
||||
|
||||
+28
-20
@@ -142,6 +142,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: should we just use GetItem?
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager, getDivingGearIfNeeded: AllowToFindDivingGear)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachtarget",
|
||||
@@ -153,27 +154,34 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// No matching items in the inventory, try to get an item
|
||||
TryAddSubObjective(ref getItemObjective, () =>
|
||||
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, equip: Equip, checkInventory: checkInventory, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
{
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = ignoredContainerIdentifiers,
|
||||
ignoredItems = containedItems,
|
||||
AllowToFindDivingGear = AllowToFindDivingGear,
|
||||
AllowDangerousPressure = AllowDangerousPressure,
|
||||
TargetCondition = ConditionLevel
|
||||
}, onAbandon: () =>
|
||||
{
|
||||
Abandon = true;
|
||||
}, onCompleted: () =>
|
||||
{
|
||||
if (getItemObjective?.TargetItem != null)
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No matching items in the inventory, try to get an item
|
||||
TryAddSubObjective(ref getItemObjective, () =>
|
||||
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, equip: Equip, checkInventory: checkInventory, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
{
|
||||
containedItems.Add(getItemObjective.TargetItem);
|
||||
}
|
||||
RemoveSubObjective(ref getItemObjective);
|
||||
});
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = ignoredContainerIdentifiers,
|
||||
ignoredItems = containedItems,
|
||||
AllowToFindDivingGear = AllowToFindDivingGear,
|
||||
AllowDangerousPressure = AllowDangerousPressure,
|
||||
TargetCondition = ConditionLevel
|
||||
}, onAbandon: () =>
|
||||
{
|
||||
Abandon = true;
|
||||
}, onCompleted: () =>
|
||||
{
|
||||
if (getItemObjective?.TargetItem != null)
|
||||
{
|
||||
containedItems.Add(getItemObjective.TargetItem);
|
||||
}
|
||||
RemoveSubObjective(ref getItemObjective);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-38
@@ -16,9 +16,12 @@ namespace Barotrauma
|
||||
private ItemContainer targetContainer;
|
||||
private readonly Item targetItem;
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
private AIObjectiveContainItem containObjective;
|
||||
|
||||
public AIObjectiveGetItem GetItemObjective => getItemObjective;
|
||||
public AIObjectiveContainItem ContainObjective => containObjective;
|
||||
|
||||
public bool Equip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -26,7 +29,7 @@ namespace Barotrauma
|
||||
/// In both cases abandons the objective.
|
||||
/// Note that has no effect if the target container was not defined (always drops) -> completes when the item is dropped.
|
||||
/// </summary>
|
||||
public bool DropIfFailsToContain { get; set; } = true;
|
||||
public bool DropIfFails { get; set; } = true;
|
||||
|
||||
public AIObjectiveDecontainItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, ItemContainer sourceContainer = null, ItemContainer targetContainer = null, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -74,54 +77,30 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (targetContainer.Inventory.Items.Contains(itemToDecontain))
|
||||
{
|
||||
if (targetContainer.Inventory.Items.Contains(itemToDecontain))
|
||||
{
|
||||
IsCompleted = true;
|
||||
return;
|
||||
}
|
||||
IsCompleted = true;
|
||||
return;
|
||||
}
|
||||
if (goToObjective == null && !itemToDecontain.IsOwnedBy(character))
|
||||
if (getItemObjective == null && !itemToDecontain.IsOwnedBy(character))
|
||||
{
|
||||
if (sourceContainer == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (!character.CanInteractWith(sourceContainer.Item, out _, checkLinked: false))
|
||||
{
|
||||
TryAddSubObjective(ref goToObjective,
|
||||
constructor: () => new AIObjectiveGoTo(sourceContainer.Item, character, objectiveManager)
|
||||
{
|
||||
// If the container changes, the item is no longer where it was
|
||||
abortCondition = () => itemToDecontain.Container != sourceContainer.Item,
|
||||
DialogueIdentifier = "dialogcannotreachtarget",
|
||||
TargetName = sourceContainer.Item.Name
|
||||
},
|
||||
onAbandon: () => Abandon = true);
|
||||
return;
|
||||
}
|
||||
TryAddSubObjective(ref getItemObjective,
|
||||
constructor: () => new AIObjectiveGetItem(character, targetItem, objectiveManager, Equip),
|
||||
onAbandon: () => Abandon = true);
|
||||
return;
|
||||
}
|
||||
if (targetContainer != null)
|
||||
{
|
||||
TryAddSubObjective(ref containObjective,
|
||||
constructor: () => new AIObjectiveContainItem(character, itemToDecontain, targetContainer, objectiveManager)
|
||||
{
|
||||
Equip = this.Equip,
|
||||
Equip = Equip,
|
||||
RemoveEmpty = false,
|
||||
GetItemPriority = this.GetItemPriority,
|
||||
GetItemPriority = GetItemPriority,
|
||||
ignoredContainerIdentifiers = sourceContainer != null ? new string[] { sourceContainer.Item.Prefab.Identifier } : null
|
||||
},
|
||||
onCompleted: () => IsCompleted = true,
|
||||
onAbandon: () =>
|
||||
{
|
||||
if (DropIfFailsToContain)
|
||||
{
|
||||
itemToDecontain.Drop(character);
|
||||
}
|
||||
Abandon = true;
|
||||
});
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -133,8 +112,17 @@ namespace Barotrauma
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
goToObjective = null;
|
||||
getItemObjective = null;
|
||||
containObjective = null;
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
base.OnAbandon();
|
||||
if (DropIfFails && targetItem != null && targetItem.IsOwnedBy(character))
|
||||
{
|
||||
targetItem.Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-12
@@ -13,6 +13,8 @@ namespace Barotrauma
|
||||
public override bool ConcurrentObjectives => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
private readonly Hull targetHull;
|
||||
|
||||
private AIObjectiveGetItem getExtinguisherObjective;
|
||||
@@ -30,12 +32,15 @@ namespace Barotrauma
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
if (!objectiveManager.IsCurrentOrder<AIObjectiveExtinguishFires>()
|
||||
&& Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
bool isOrder = objectiveManager.IsCurrentOrder<AIObjectiveExtinguishFires>();
|
||||
if (!isOrder && Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
|
||||
{
|
||||
// Don't go into rooms with any enemies, unless it's an order
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -43,14 +48,22 @@ namespace Barotrauma
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
|
||||
if (targetHull == character.CurrentHull)
|
||||
if (targetHull == character.CurrentHull || HumanAIController.VisibleHulls.Contains(targetHull))
|
||||
{
|
||||
distanceFactor = 1;
|
||||
}
|
||||
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
|
||||
float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1));
|
||||
if (severity > 0.5f && !isOrder)
|
||||
{
|
||||
// Ignore severe fires unless ordered. (Let the fire drain all the oxygen instead).
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
float devotion = CumulatedDevotion / 100;
|
||||
Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1));
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
@@ -131,13 +144,16 @@ namespace Barotrauma
|
||||
if (move)
|
||||
{
|
||||
//go to the first firesource
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range / 2)
|
||||
if (TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(fs, character, objectiveManager, closeEnough: extinguisher.Range / 2)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire",
|
||||
TargetName = fs.Hull.DisplayName
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective)))
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachfire",
|
||||
TargetName = fs.Hull.DisplayName
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref gotoObjective));
|
||||
gotoObjective.requiredCondition = () => HumanAIController.VisibleHulls.Contains(fs.Hull);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+14
-19
@@ -1,6 +1,8 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -8,14 +10,22 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "extinguish fires";
|
||||
public override bool ForceRun => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public AIObjectiveExtinguishFires(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { }
|
||||
|
||||
protected override bool Filter(Hull hull) => IsValidTarget(hull, character);
|
||||
|
||||
protected override float TargetEvaluation() => Targets.Sum(t => GetFireSeverity(t));
|
||||
protected override float TargetEvaluation() =>
|
||||
// If any target is visible -> 100 priority
|
||||
Targets.Any(t => t == character.CurrentHull || HumanAIController.VisibleHulls.Contains(t)) ? 100 :
|
||||
// Else based on the fire severity
|
||||
Targets.Sum(t => GetFireSeverity(t) * 100);
|
||||
|
||||
public static float GetFireSeverity(Hull hull) => hull.FireSources.Sum(fs => fs.Size.X);
|
||||
/// <summary>
|
||||
/// 0-1 based on the horizontal size of all of the fires in the hull.
|
||||
/// </summary>
|
||||
public static float GetFireSeverity(Hull hull) => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, Math.Min(hull.Rect.Width, 1000), hull.FireSources.Sum(fs => fs.Size.X)));
|
||||
|
||||
protected override IEnumerable<Hull> GetList() => Hull.hullList;
|
||||
|
||||
@@ -31,22 +41,7 @@ namespace Barotrauma
|
||||
if (hull.FireSources.None()) { return false; }
|
||||
if (hull.Submarine == null) { return false; }
|
||||
if (character.Submarine == null) { return false; }
|
||||
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { return false; }
|
||||
if (character.AIController is HumanAIController humanAI)
|
||||
{
|
||||
if (hull.Submarine.TeamID != character.TeamID)
|
||||
{
|
||||
if (humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveExtinguishFires>())
|
||||
{
|
||||
// For orders, allow targets in the current sub (for example if the bot is inside an outpost or a wreck)
|
||||
if (hull.Submarine != character.Submarine) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(hull, includingConnectedSubs: true)) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ namespace Barotrauma
|
||||
private Item targetItem;
|
||||
|
||||
public static float MIN_OXYGEN = 10;
|
||||
public static string HEAVY_DIVING_GEAR = "heavydiving";
|
||||
public static string HEAVY_DIVING_GEAR = "deepdiving";
|
||||
public static string LIGHT_DIVING_GEAR = "lightdiving";
|
||||
public static string OXYGEN_SOURCE = "oxygensource";
|
||||
|
||||
|
||||
+2
-1
@@ -14,8 +14,9 @@ namespace Barotrauma
|
||||
public override bool IgnoreUnsafeHulls => true;
|
||||
public override bool ConcurrentObjectives => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => false;
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + Environment.StackTrace); }
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + Environment.StackTrace.CleanupStackTrace()); }
|
||||
|
||||
// TODO: expose?
|
||||
const float priorityIncrease = 100;
|
||||
|
||||
+13
-7
@@ -12,6 +12,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => "fix leak";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public Gap Leak { get; private set; }
|
||||
|
||||
@@ -35,11 +36,7 @@ namespace Barotrauma
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
return Priority;
|
||||
}
|
||||
if (Leak.Removed || Leak.Open <= 0)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
}
|
||||
else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.ObjectiveManager.GetActiveObjective<AIObjectiveFixLeak>()?.Leak == Leak))
|
||||
{
|
||||
@@ -116,7 +113,7 @@ namespace Barotrauma
|
||||
{
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
float reach = repairTool.Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength);
|
||||
float reach = CalculateReach(repairTool, character);
|
||||
bool canOperate = toLeak.LengthSquared() < reach * reach;
|
||||
if (canOperate)
|
||||
{
|
||||
@@ -144,7 +141,7 @@ namespace Barotrauma
|
||||
onAbandon: () =>
|
||||
{
|
||||
if (Check()) { IsCompleted = true; }
|
||||
else if ((Leak.WorldPosition - character.WorldPosition).LengthSquared() > reach * reach * 2)
|
||||
else if ((Leak.WorldPosition - character.WorldPosition).LengthSquared() > MathUtils.Pow(reach * 2, 2))
|
||||
{
|
||||
// Too far
|
||||
Abandon = true;
|
||||
@@ -167,5 +164,14 @@ namespace Barotrauma
|
||||
gotoObjective = null;
|
||||
operateObjective = null;
|
||||
}
|
||||
|
||||
public static float CalculateReach(RepairTool repairTool, Character character)
|
||||
{
|
||||
float armLength = ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength);
|
||||
// This is an approximation, because we don't know the exact reach until the pose is taken.
|
||||
// And even then the actual range depends on the direction we are aiming to.
|
||||
// Found out that without any multiplier the value (209) is often too short.
|
||||
return repairTool.Range + armLength * 1.2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -9,6 +9,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => "fix leaks";
|
||||
public override bool ForceRun => true;
|
||||
public override bool KeepDivingGearOn => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
private Hull PrioritizedHull { get; set; }
|
||||
|
||||
public AIObjectiveFixLeaks(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1, Hull prioritizedHull = null) : base(character, objectiveManager, priorityModifier)
|
||||
@@ -71,12 +72,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (gap == null) { return false; }
|
||||
if (gap.ConnectedWall == null || gap.ConnectedDoor != null || gap.Open <= 0 || gap.linkedTo.All(l => l == null)) { return false; }
|
||||
if (gap.Submarine == null) { return false; }
|
||||
if (gap.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (!character.Submarine.IsConnectedTo(gap.Submarine)) { return false; }
|
||||
}
|
||||
if (gap.Submarine == null || character.Submarine == null) { return false; }
|
||||
// Don't allow going into another sub, unless it's connected and of the same team and type.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(gap, includingConnectedSubs: true)) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+44
-13
@@ -37,6 +37,7 @@ namespace Barotrauma
|
||||
public static float DefaultReach = 100;
|
||||
|
||||
public bool AllowToFindDivingGear { get; set; } = true;
|
||||
public bool MustBeSpecificItem { get; set; }
|
||||
|
||||
public AIObjectiveGetItem(Character character, Item targetItem, AIObjectiveManager objectiveManager, bool equip = true, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -84,6 +85,11 @@ namespace Barotrauma
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
Abandon = true;
|
||||
return;
|
||||
}
|
||||
if (identifiersOrTags != null && !isDoneSeeking)
|
||||
{
|
||||
if (checkInventory)
|
||||
@@ -109,7 +115,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
FindTargetItem();
|
||||
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
||||
if (!objectiveManager.IsCurrentOrder<AIObjectiveGoTo>())
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +146,8 @@ namespace Barotrauma
|
||||
if (originalTarget == null)
|
||||
{
|
||||
// Try again
|
||||
Reset();
|
||||
ignoredItems.Add(targetItem);
|
||||
ResetInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,12 +186,8 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (HumanAIController.TryToMoveItem(targetItem, character.Inventory))
|
||||
if (HumanAIController.TakeItem(targetItem, character.Inventory, equip, storeUnequipped: true))
|
||||
{
|
||||
if (equip)
|
||||
{
|
||||
targetItem.Equip(character);
|
||||
}
|
||||
IsCompleted = true;
|
||||
}
|
||||
else
|
||||
@@ -207,8 +213,16 @@ namespace Barotrauma
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
ignoredItems.Add(targetItem);
|
||||
Reset();
|
||||
if (originalTarget == null)
|
||||
{
|
||||
// Try again
|
||||
ignoredItems.Add(targetItem);
|
||||
ResetInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
Abandon = true;
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective));
|
||||
}
|
||||
@@ -235,13 +249,13 @@ namespace Barotrauma
|
||||
Submarine mySub = character.Submarine;
|
||||
if (itemSub == null) { continue; }
|
||||
if (mySub == null) { continue; }
|
||||
if (itemSub.TeamID != mySub.TeamID && itemSub.TeamID != character.TeamID) { continue; }
|
||||
if (!CheckItem(item)) { continue; }
|
||||
if (ignoredContainerIdentifiers != null && item.Container != null)
|
||||
{
|
||||
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
|
||||
}
|
||||
if (!mySub.IsConnectedTo(itemSub)) { continue; }
|
||||
// Don't allow going into another sub, unless it's connected and of the same team and type.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { continue; }
|
||||
if (character.IsItemTakenBySomeoneElse(item)) { continue; }
|
||||
float itemPriority = 1;
|
||||
if (GetItemPriority != null)
|
||||
@@ -272,7 +286,7 @@ namespace Barotrauma
|
||||
if (!(MapEntityPrefab.List.FirstOrDefault(me => me is ItemPrefab ip && identifiersOrTags.Any(id => id == ip.Identifier || ip.Tags.Contains(id))) is ItemPrefab prefab))
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s): {string.Join(", ", identifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
|
||||
#endif
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -291,7 +305,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s): {string.Join(", ", identifiersOrTags)}", Color.Yellow);
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot find the item with the following identifier(s) or tag(s): {string.Join(", ", identifiersOrTags)}", Color.Yellow);
|
||||
#endif
|
||||
Abandon = true;
|
||||
}
|
||||
@@ -330,11 +344,28 @@ namespace Barotrauma
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
ResetInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does not reset the ignored items list
|
||||
/// </summary>
|
||||
private void ResetInternal()
|
||||
{
|
||||
goToObjective = null;
|
||||
targetItem = originalTarget;
|
||||
moveToTarget = targetItem?.GetRootInventoryOwner();
|
||||
isDoneSeeking = false;
|
||||
currSearchIndex = 0;
|
||||
}
|
||||
|
||||
protected override void OnAbandon()
|
||||
{
|
||||
base.OnAbandon();
|
||||
if (objectiveManager.CurrentOrder != null)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogCannotFindItem"), null, 0.0f, "cannotfinditem", 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
-15
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -50,6 +51,7 @@ namespace Barotrauma
|
||||
public override bool AbandonWhenCannotCompleteSubjectives => !repeat;
|
||||
|
||||
public override bool AllowOutsideSubmarine => AllowGoingOutside;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public string DialogueIdentifier { get; set; }
|
||||
public string TargetName { get; set; }
|
||||
@@ -60,17 +62,27 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
bool isOrder = objectiveManager.CurrentOrder == this;
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
return Priority;
|
||||
}
|
||||
if (followControlledCharacter && Character.Controlled == null)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
}
|
||||
if (Target is Entity e && e.Removed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
}
|
||||
if (IgnoreIfTargetDead && Target is Character character && character.IsDead)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -84,7 +96,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Priority = objectiveManager.CurrentOrder == this ? AIObjectiveManager.OrderPriority : 10;
|
||||
Priority = isOrder ? AIObjectiveManager.OrderPriority : 10;
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
@@ -93,7 +105,7 @@ namespace Barotrauma
|
||||
public AIObjectiveGoTo(ISpatialEntity target, Character character, AIObjectiveManager objectiveManager, bool repeat = false, bool getDivingGearIfNeeded = true, float priorityModifier = 1, float closeEnough = 0)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
this.Target = target;
|
||||
Target = target;
|
||||
this.repeat = repeat;
|
||||
waitUntilPathUnreachable = 3.0f;
|
||||
this.getDivingGearIfNeeded = getDivingGearIfNeeded;
|
||||
@@ -260,6 +272,53 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!character.AnimController.InWater)
|
||||
{
|
||||
useScooter = false;
|
||||
checkScooterTimer = 0;
|
||||
}
|
||||
else if (checkScooterTimer <= 0)
|
||||
{
|
||||
useScooter = false;
|
||||
checkScooterTimer = checkScooterTime;
|
||||
string scooterTag = "scooter";
|
||||
string batteryTag = "mobilebattery";
|
||||
Item scooter = null;
|
||||
bool isScooterEquipped = false;
|
||||
float closeEnough = 250;
|
||||
float squaredDistance = Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition);
|
||||
bool shouldUseScooter = squaredDistance > closeEnough * closeEnough && (!mimic ||
|
||||
(Target is Character targetCharacter && targetCharacter.HasEquippedItem(scooterTag, allowBroken: false)) || squaredDistance > Math.Pow(closeEnough * 2, 2));
|
||||
if (HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> equippedScooters, batteryTag, requireEquipped: true))
|
||||
{
|
||||
scooter = equippedScooters.FirstOrDefault();
|
||||
isScooterEquipped = scooter != null;
|
||||
}
|
||||
else if (shouldUseScooter && HumanAIController.HasItem(character, scooterTag, out IEnumerable<Item> scooters, batteryTag, requireEquipped: false))
|
||||
{
|
||||
scooter = scooters.FirstOrDefault();
|
||||
if (scooter != null)
|
||||
{
|
||||
isScooterEquipped = HumanAIController.TakeItem(scooter, character.Inventory, equip: true, dropOtherIfCannotMove: false, allowSwapping: true, storeUnequipped: false);
|
||||
}
|
||||
}
|
||||
if (scooter != null && isScooterEquipped)
|
||||
{
|
||||
if (shouldUseScooter)
|
||||
{
|
||||
useScooter = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unequip
|
||||
character.Inventory.TryPutItem(scooter, character, CharacterInventory.anySlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
checkScooterTimer -= deltaTime;
|
||||
}
|
||||
if (SteeringManager == PathSteering)
|
||||
{
|
||||
Func<PathNode, bool> nodeFilter = null;
|
||||
@@ -274,47 +333,90 @@ namespace Barotrauma
|
||||
}, endNodeFilter, nodeFilter, CheckVisibility);
|
||||
if (!isInside && PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(Target.WorldPosition - character.WorldPosition));
|
||||
if (character.AnimController.InWater)
|
||||
if (useScooter)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
UseScooter(Target.WorldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(Target.WorldPosition - character.WorldPosition));
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (useScooter && PathSteering.CurrentPath?.CurrentNode != null)
|
||||
{
|
||||
UseScooter(PathSteering.CurrentPath.CurrentNode.WorldPosition);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
if (useScooter)
|
||||
{
|
||||
UseScooter(Target.WorldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UseScooter(Vector2 targetWorldPos)
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
character.CursorPosition = targetWorldPos;
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
character.CursorPosition -= character.Submarine.Position;
|
||||
}
|
||||
Vector2 dir = Vector2.Normalize(character.CursorPosition - character.Position);
|
||||
if (!MathUtils.IsValid(dir)) { dir = Vector2.UnitY; }
|
||||
SteeringManager.SteeringManual(1.0f, dir);
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Hull GetTargetHull()
|
||||
private bool useScooter;
|
||||
private float checkScooterTimer;
|
||||
private readonly float checkScooterTime = 0.2f;
|
||||
|
||||
public Hull GetTargetHull() => GetTargetHull(Target);
|
||||
|
||||
public static Hull GetTargetHull(ISpatialEntity target)
|
||||
{
|
||||
if (Target is Hull h)
|
||||
if (target is Hull h)
|
||||
{
|
||||
return h;
|
||||
}
|
||||
else if (Target is Item i)
|
||||
else if (target is Item i)
|
||||
{
|
||||
return i.CurrentHull;
|
||||
}
|
||||
else if (Target is Character c)
|
||||
else if (target is Character c)
|
||||
{
|
||||
return c.CurrentHull;
|
||||
}
|
||||
else if (Target is Gap g)
|
||||
else if (target is Gap g)
|
||||
{
|
||||
return g.FlowTargetHull;
|
||||
}
|
||||
else if (Target is WayPoint wp)
|
||||
else if (target is WayPoint wp)
|
||||
{
|
||||
return wp.CurrentHull;
|
||||
}
|
||||
else if (Target is FireSource fs)
|
||||
else if (target is FireSource fs)
|
||||
{
|
||||
return fs.Hull;
|
||||
}
|
||||
else if (target is OrderTarget ot)
|
||||
{
|
||||
return ot.Hull;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -361,7 +463,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Target is Item item)
|
||||
{
|
||||
if (character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
|
||||
if (!character.IsClimbing && character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
|
||||
}
|
||||
else if (Target is Character targetCharacter)
|
||||
{
|
||||
|
||||
+90
-48
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -11,14 +12,14 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "idle";
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
private BehaviorType behavior;
|
||||
public BehaviorType Behavior
|
||||
{
|
||||
get { return behavior; }
|
||||
set
|
||||
{
|
||||
set
|
||||
{
|
||||
behavior = value;
|
||||
switch (behavior)
|
||||
{
|
||||
@@ -27,16 +28,13 @@ namespace Barotrauma
|
||||
newTargetIntervalMax = 20;
|
||||
standStillMin = 2;
|
||||
standStillMax = 10;
|
||||
walkDurationMin = 5;
|
||||
walkDurationMax = 10;
|
||||
break;
|
||||
case BehaviorType.Passive:
|
||||
case BehaviorType.StayInHull:
|
||||
newTargetIntervalMin = 60;
|
||||
newTargetIntervalMax = 120;
|
||||
standStillMin = 30;
|
||||
standStillMax = 60;
|
||||
walkDurationMin = 5;
|
||||
walkDurationMax = 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -46,8 +44,8 @@ namespace Barotrauma
|
||||
private float newTargetIntervalMax;
|
||||
private float standStillMin;
|
||||
private float standStillMax;
|
||||
private float walkDurationMin;
|
||||
private float walkDurationMax;
|
||||
private readonly float walkDurationMin = 5;
|
||||
private readonly float walkDurationMax = 10;
|
||||
|
||||
public enum BehaviorType
|
||||
{
|
||||
@@ -55,7 +53,7 @@ namespace Barotrauma
|
||||
Passive,
|
||||
StayInHull
|
||||
}
|
||||
|
||||
public Hull TargetHull { get; set; }
|
||||
private Hull currentTarget;
|
||||
private float newTargetTimer;
|
||||
|
||||
@@ -86,7 +84,7 @@ namespace Barotrauma
|
||||
protected override bool Check() => false;
|
||||
public override bool CanBeCompleted => true;
|
||||
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + Environment.StackTrace); }
|
||||
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>();
|
||||
|
||||
@@ -142,6 +140,8 @@ namespace Barotrauma
|
||||
timerMargin = 0;
|
||||
}
|
||||
|
||||
private bool IsSteeringFinished() => PathSteering.CurrentPath != null && (PathSteering.CurrentPath.Finished || PathSteering.CurrentPath.Unreachable);
|
||||
|
||||
protected override void Act(float deltaTime)
|
||||
{
|
||||
if (PathSteering == null) { return; }
|
||||
@@ -164,12 +164,30 @@ namespace Barotrauma
|
||||
character.DeselectCharacter();
|
||||
}
|
||||
|
||||
if (behavior != BehaviorType.StayInHull)
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
|
||||
bool IsSteeringFinished() => PathSteering.CurrentPath != null && (PathSteering.CurrentPath.Finished || PathSteering.CurrentPath.Unreachable);
|
||||
CleanupItems(deltaTime);
|
||||
|
||||
if (behavior == BehaviorType.StayInHull)
|
||||
{
|
||||
currentTarget = TargetHull;
|
||||
bool stayInHull = character.CurrentHull == currentTarget && IsSteeringFinished() && !character.IsClimbing;
|
||||
if (stayInHull)
|
||||
{
|
||||
Wander(deltaTime);
|
||||
}
|
||||
else if (currentTarget != null)
|
||||
{
|
||||
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
|
||||
if (currentTarget != null && !currentTargetIsInvalid)
|
||||
{
|
||||
@@ -226,7 +244,7 @@ namespace Barotrauma
|
||||
searchingNewHull = true;
|
||||
return;
|
||||
}
|
||||
else if (targetHulls.Count > 0)
|
||||
else if (targetHulls.Any())
|
||||
{
|
||||
//choose a random available hull
|
||||
currentTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
|
||||
@@ -242,12 +260,13 @@ namespace Barotrauma
|
||||
});
|
||||
if (path.Unreachable)
|
||||
{
|
||||
//can't go to this room, remove it from the list and try another room next frame
|
||||
//can't go to this room, remove it from the list and try another room
|
||||
int index = targetHulls.IndexOf(currentTarget);
|
||||
targetHulls.RemoveAt(index);
|
||||
hullWeights.RemoveAt(index);
|
||||
PathSteering.Reset();
|
||||
currentTarget = null;
|
||||
SetTargetTimerLow();
|
||||
return;
|
||||
}
|
||||
searchingNewHull = false;
|
||||
@@ -263,47 +282,25 @@ namespace Barotrauma
|
||||
{
|
||||
character.AIController.SelectTarget(currentTarget.AiTarget);
|
||||
string errorMsg = null;
|
||||
#if DEBUG
|
||||
#if DEBUG
|
||||
bool isRoomNameFound = currentTarget.DisplayName != null;
|
||||
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")";
|
||||
#endif
|
||||
#endif
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsgStr: errorMsg, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
PathSteering.SetPath(path);
|
||||
}
|
||||
SetTargetTimerNormal();
|
||||
}
|
||||
}
|
||||
newTargetTimer -= deltaTime;
|
||||
}
|
||||
|
||||
//wander randomly
|
||||
// - if reached the end of the path
|
||||
// - if the target is unreachable
|
||||
// - if the path requires going outside
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
if (behavior == BehaviorType.StayInHull || SteeringManager != PathSteering || (PathSteering.CurrentPath != null &&
|
||||
(PathSteering.CurrentPath.Finished || PathSteering.CurrentPath.Unreachable || PathSteering.CurrentPath.HasOutdoorsNodes)))
|
||||
if (!character.IsClimbing && IsSteeringFinished())
|
||||
{
|
||||
Wander(deltaTime);
|
||||
return;
|
||||
}
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
|
||||
if (currentTarget != null)
|
||||
{
|
||||
if (SteeringManager == PathSteering)
|
||||
else if (currentTarget != null)
|
||||
{
|
||||
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(currentTarget));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Wander(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +325,7 @@ namespace Barotrauma
|
||||
{
|
||||
//if there are characters too close on both sides, don't try to steer away from them
|
||||
//because it'll cause the character to spaz out trying to avoid both
|
||||
if (tooCloseCharacter != null &&
|
||||
if (tooCloseCharacter != null &&
|
||||
Math.Sign(tooCloseCharacter.WorldPosition.X - character.WorldPosition.X) != Math.Sign(c.WorldPosition.X - character.WorldPosition.X))
|
||||
{
|
||||
tooCloseCharacter = null;
|
||||
@@ -336,7 +333,7 @@ namespace Barotrauma
|
||||
}
|
||||
tooCloseCharacter = c;
|
||||
}
|
||||
HumanAIController.FaceTarget(c);
|
||||
HumanAIController.FaceTarget(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +341,7 @@ namespace Barotrauma
|
||||
{
|
||||
Vector2 diff = character.WorldPosition - tooCloseCharacter.WorldPosition;
|
||||
if (diff.LengthSquared() < 0.0001f) { diff = Rand.Vector(1.0f); }
|
||||
if (Math.Abs(diff.X) > 0 &&
|
||||
if (Math.Abs(diff.X) > 0 &&
|
||||
(character.WorldPosition.X > currentHull.WorldRect.Right - 50 || character.WorldPosition.X < currentHull.WorldRect.Left + 50))
|
||||
{
|
||||
// Between a wall and a character -> move away
|
||||
@@ -459,6 +456,48 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
#region Cleaning
|
||||
private readonly float checkItemsInterval = 1;
|
||||
private float checkItemsTimer;
|
||||
private readonly List<Item> itemsToClean = new List<Item>();
|
||||
private readonly List<Item> ignoredItems = new List<Item>();
|
||||
|
||||
private void CleanupItems(float deltaTime)
|
||||
{
|
||||
if (checkItemsTimer <= 0)
|
||||
{
|
||||
checkItemsTimer = checkItemsInterval * Rand.Range(0.9f, 1.1f);
|
||||
var hull = character.CurrentHull;
|
||||
if (hull != null)
|
||||
{
|
||||
itemsToClean.Clear();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.CurrentHull != hull) { continue; }
|
||||
if (AIObjectiveCleanupItems.IsValidTarget(item, character) && !ignoredItems.Contains(item))
|
||||
{
|
||||
itemsToClean.Add(item);
|
||||
}
|
||||
}
|
||||
if (itemsToClean.Any())
|
||||
{
|
||||
var targetItem = itemsToClean.OrderBy(i => Math.Abs(character.WorldPosition.X - i.WorldPosition.X)).FirstOrDefault();
|
||||
if (targetItem != null)
|
||||
{
|
||||
var cleanupObjective = new AIObjectiveCleanupItem(targetItem, character, objectiveManager, PriorityModifier);
|
||||
cleanupObjective.Abandoned += () => ignoredItems.Add(targetItem);
|
||||
subObjectives.Add(cleanupObjective);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
checkItemsTimer -= deltaTime;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static bool IsForbidden(Hull hull)
|
||||
{
|
||||
if (hull == null) { return true; }
|
||||
@@ -475,6 +514,9 @@ namespace Barotrauma
|
||||
tooCloseCharacter = null;
|
||||
targetHulls.Clear();
|
||||
hullWeights.Clear();
|
||||
checkItemsTimer = 0;;
|
||||
itemsToClean.Clear();
|
||||
ignoredItems.Clear();
|
||||
autonomousObjectiveRetryTimer = 10;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -46,7 +45,7 @@ namespace Barotrauma
|
||||
public override bool AllowSubObjectiveSorting => true;
|
||||
public virtual bool InverseTargetEvaluation => false;
|
||||
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + System.Environment.StackTrace); }
|
||||
public override bool IsLoop { get => true; set => throw new Exception("Trying to set the value for IsLoop from: " + System.Environment.StackTrace.CleanupStackTrace()); }
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -79,7 +78,12 @@ namespace Barotrauma
|
||||
var target = objective.Key;
|
||||
if (!Targets.Contains(target))
|
||||
{
|
||||
subObjectives.Remove(objective.Value);
|
||||
var subObjective = objective.Value;
|
||||
if (CurrentSubObjective == subObjective)
|
||||
{
|
||||
CurrentSubObjective.Abandon = !CurrentSubObjective.IsCompleted;
|
||||
}
|
||||
subObjectives.Remove(subObjective);
|
||||
}
|
||||
}
|
||||
SyncRemovedObjectives(Objectives, GetList());
|
||||
@@ -137,7 +141,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (objectiveManager.CurrentOrder == this)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
Priority = ForceOrderPriority ? AIObjectiveManager.OrderPriority : targetValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+32
-6
@@ -70,7 +70,7 @@ namespace Barotrauma
|
||||
if (objective == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace);
|
||||
DebugConsole.ThrowError("Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -128,8 +128,7 @@ namespace Barotrauma
|
||||
var orderPrefab = Order.GetPrefab(autonomousObjective.identifier);
|
||||
if (orderPrefab == null) { throw new Exception($"Could not find a matching prefab by the identifier: '{autonomousObjective.identifier}'"); }
|
||||
var item = orderPrefab.MustSetTarget ? orderPrefab.GetMatchingItems(character.Submarine, mustBelongToPlayerSub: false, requiredTeam: character.Info.TeamID)?.GetRandom() : null;
|
||||
var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity,
|
||||
item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType), orderGiver: character);
|
||||
var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity, orderPrefab.GetTargetItemComponent(item), orderGiver: character);
|
||||
if (order == null) { continue; }
|
||||
if (autonomousObjective.ignoreAtOutpost && Level.IsLoadedOutpost && character.TeamID != Character.TeamType.FriendlyNPC) { continue; }
|
||||
var objective = CreateObjective(order, autonomousObjective.option, character, isAutonomous: true, autonomousObjective.priorityModifier);
|
||||
@@ -147,7 +146,7 @@ namespace Barotrauma
|
||||
if (objective == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"{character.Name}: Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace);
|
||||
DebugConsole.ThrowError($"{character.Name}: Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -314,9 +313,10 @@ namespace Barotrauma
|
||||
};
|
||||
break;
|
||||
case "wait":
|
||||
newObjective = new AIObjectiveGoTo(order.TargetEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||
newObjective = new AIObjectiveGoTo(order.TargetSpatialEntity ?? character, character, this, repeat: true, priorityModifier: priorityModifier)
|
||||
{
|
||||
AllowGoingOutside = character.CurrentHull == null
|
||||
AllowGoingOutside = order.TargetSpatialEntity == null ? character.CurrentHull == null :
|
||||
character.Submarine == null || character.Submarine != order.TargetSpatialEntity.Submarine
|
||||
};
|
||||
break;
|
||||
case "fixleaks":
|
||||
@@ -374,6 +374,32 @@ namespace Barotrauma
|
||||
Override = orderGiver != null && orderGiver.IsPlayer
|
||||
};
|
||||
break;
|
||||
case "setchargepct":
|
||||
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, false, priorityModifier: priorityModifier)
|
||||
{
|
||||
IsLoop = false,
|
||||
Override = character.CurrentOrder != null,
|
||||
completionCondition = () =>
|
||||
{
|
||||
if (float.TryParse(option, out float pct))
|
||||
{
|
||||
var targetRatio = Math.Clamp(pct, 0f, 1f);
|
||||
var currentRatio = (order.TargetItemComponent as PowerContainer).RechargeRatio;
|
||||
return Math.Abs(targetRatio - currentRatio) < 0.05f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
break;
|
||||
case "getitem":
|
||||
newObjective = new AIObjectiveGetItem(character, order.TargetEntity as Item ?? order.TargetItemComponent?.Item, this, false, priorityModifier: priorityModifier)
|
||||
{
|
||||
MustBeSpecificItem = true
|
||||
};
|
||||
break;
|
||||
case "cleanupitems":
|
||||
newObjective = new AIObjectiveCleanupItems(character, this, priorityModifier, order.TargetEntity as Item);
|
||||
break;
|
||||
default:
|
||||
if (order.TargetItemComponent == null) { return null; }
|
||||
if (order.TargetItemComponent.Item.NonInteractable) { return null; }
|
||||
|
||||
+6
-3
@@ -11,6 +11,7 @@ namespace Barotrauma
|
||||
public override string DebugTag => $"operate item {component.Name}";
|
||||
public override bool AllowAutomaticItemUnequipping => true;
|
||||
public override bool AllowMultipleInstances => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
private ItemComponent component, controller;
|
||||
private Entity operateTarget;
|
||||
@@ -35,9 +36,11 @@ namespace Barotrauma
|
||||
|
||||
public override float GetPriority()
|
||||
{
|
||||
bool isOrder = objectiveManager.CurrentOrder == this;
|
||||
if (!IsAllowed || character.LockHands)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = !isOrder;
|
||||
return Priority;
|
||||
}
|
||||
if (component.Item.ConditionPercentage <= 0)
|
||||
@@ -46,7 +49,6 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isOrder = objectiveManager.CurrentOrder == this;
|
||||
if (isOrder)
|
||||
{
|
||||
Priority = AIObjectiveManager.OrderPriority;
|
||||
@@ -172,7 +174,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (target.CanBeSelected)
|
||||
{
|
||||
if (character.CanInteractWith(target.Item, out _, checkLinked: false))
|
||||
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
|
||||
{
|
||||
HumanAIController.FaceTarget(target.Item);
|
||||
if (character.SelectedConstruction != target.Item)
|
||||
@@ -189,7 +191,8 @@ namespace Barotrauma
|
||||
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager, closeEnough: 50)
|
||||
{
|
||||
DialogueIdentifier = "dialogcannotreachtarget",
|
||||
TargetName = target.Item.Name
|
||||
TargetName = target.Item.Name,
|
||||
endNodeFilter = node => node.Waypoint.Ladders == null
|
||||
},
|
||||
onAbandon: () => Abandon = true,
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective));
|
||||
|
||||
+10
-3
@@ -10,6 +10,8 @@ namespace Barotrauma
|
||||
{
|
||||
public override string DebugTag => "repair item";
|
||||
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public Item Item { get; private set; }
|
||||
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
@@ -34,6 +36,7 @@ namespace Barotrauma
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
// TODO: priority list?
|
||||
@@ -69,7 +72,7 @@ namespace Barotrauma
|
||||
IsCompleted = Item.IsFullCondition;
|
||||
if (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, true), null, 0.0f, "itemrepaired", 10.0f);
|
||||
}
|
||||
return IsCompleted;
|
||||
}
|
||||
@@ -134,7 +137,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (character.CanInteractWith(Item, out _, checkLinked: false))
|
||||
if (!character.IsClimbing && character.CanInteractWith(Item, out _, checkLinked: false))
|
||||
{
|
||||
HumanAIController.FaceTarget(Item);
|
||||
if (repairTool != null)
|
||||
@@ -235,7 +238,11 @@ namespace Barotrauma
|
||||
|
||||
private void OperateRepairTool(float deltaTime)
|
||||
{
|
||||
character.CursorPosition = Item.Position;
|
||||
character.CursorPosition = Item.WorldPosition;
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
character.CursorPosition -= character.Submarine.Position;
|
||||
}
|
||||
if (repairTool.Item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
|
||||
+4
-8
@@ -24,11 +24,11 @@ namespace Barotrauma
|
||||
private readonly Item prioritizedItem;
|
||||
|
||||
public override bool AllowMultipleInstances => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
public readonly static float RequiredSuccessFactor = 0.4f;
|
||||
|
||||
public override bool IsDuplicate<T>(T otherObjective) =>
|
||||
(otherObjective as AIObjective) is AIObjectiveRepairItems repairObjective && repairObjective.RequireAdequateSkills == RequireAdequateSkills;
|
||||
public override bool IsDuplicate<T>(T otherObjective) => otherObjective is AIObjectiveRepairItems repairObjective && repairObjective.RequireAdequateSkills == RequireAdequateSkills;
|
||||
|
||||
public AIObjectiveRepairItems(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1, Item prioritizedItem = null)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -151,13 +151,9 @@ namespace Barotrauma
|
||||
if (item.NonInteractable) { return false; }
|
||||
if (item.IsFullCondition) { return false; }
|
||||
if (item.CurrentHull == null) { return false; }
|
||||
if (item.Submarine == null) { return false; }
|
||||
if (item.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (item.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return false; }
|
||||
if (item.Repairables.None()) { return false; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -14,6 +14,7 @@ namespace Barotrauma
|
||||
public override bool KeepDivingGearOn => true;
|
||||
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
const float TreatmentDelay = 0.5f;
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (targetCharacter == null)
|
||||
{
|
||||
string errorMsg = $"{character.Name}: Attempted to create a Rescue objective with no target!\n" + Environment.StackTrace;
|
||||
string errorMsg = $"{character.Name}: Attempted to create a Rescue objective with no target!\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("AIObjectiveRescue:ctor:targetnull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
Abandon = true;
|
||||
@@ -392,11 +393,13 @@ namespace Barotrauma
|
||||
if (!IsAllowed)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ namespace Barotrauma
|
||||
public override bool ForceRun => true;
|
||||
public override bool InverseTargetEvaluation => true;
|
||||
public override bool AllowOutsideSubmarine => true;
|
||||
public override bool AllowInAnySub => true;
|
||||
|
||||
private const float vitalityThreshold = 75;
|
||||
private const float vitalityThresholdForOrders = 85;
|
||||
|
||||
Reference in New Issue
Block a user