Merge branch 'dev' of https://github.com/Regalis11/Barotrauma into unstable

This commit is contained in:
EvilFactory
2022-09-29 12:13:55 -03:00
602 changed files with 19759 additions and 16312 deletions
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -529,6 +530,15 @@ namespace Barotrauma
}
return canEquip;
}
protected bool CheckItemIdentifiersOrTags(Item item, ImmutableHashSet<Identifier> identifiersOrTags)
{
if (identifiersOrTags.Contains(item.Prefab.Identifier)) { return true; }
foreach (var identifier in identifiersOrTags)
{
if (item.HasTag(identifier)) { return true; }
}
return false;
}
protected bool CanEquip(Item item) => CanEquip(character, item);
}
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using FarseerPhysics.Dynamics;
using static Barotrauma.AIObjectiveFindSafety;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -512,6 +513,23 @@ namespace Barotrauma
foreach (var weapon in weaponList)
{
float priority = weapon.CombatPriority;
if (weapon is RepairTool repairTool)
{
switch (repairTool.UsableIn)
{
case RepairTool.UseEnvironment.Air:
if (character.InWater) { continue; }
break;
case RepairTool.UseEnvironment.Water:
if (!character.InWater) { continue; }
break;
case RepairTool.UseEnvironment.None:
continue;
case RepairTool.UseEnvironment.Both:
default:
break;
}
}
if (prioritizeMelee)
{
if (weapon is MeleeWeapon)
@@ -895,11 +913,14 @@ namespace Barotrauma
private void RemoveFollowTarget()
{
if (arrestingRegistered)
if (followTargetObjective != null)
{
followTargetObjective.Completed -= OnArrestTargetReached;
if (arrestingRegistered)
{
followTargetObjective.Completed -= OnArrestTargetReached;
}
RemoveSubObjective(ref followTargetObjective);
}
RemoveSubObjective(ref followTargetObjective);
arrestingRegistered = false;
}
@@ -950,7 +971,7 @@ namespace Barotrauma
/// <summary>
/// Seeks for more ammunition. Creates a new subobjective.
/// </summary>
private void SeekAmmunition(Identifier[] ammunitionIdentifiers)
private void SeekAmmunition(ImmutableHashSet<Identifier> ammunitionIdentifiers)
{
retreatTarget = null;
RemoveSubObjective(ref retreatObjective);
@@ -985,7 +1006,7 @@ namespace Barotrauma
HumanAIController.UnequipEmptyItems(Weapon);
RelatedItem item = null;
Item ammunition = null;
Identifier[] ammunitionIdentifiers = null;
ImmutableHashSet<Identifier> ammunitionIdentifiers = null;
if (WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
{
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
@@ -1011,8 +1032,8 @@ namespace Barotrauma
if (ammunitionIdentifiers != null)
{
// Try reload ammunition from inventory
bool IsInsideHeadset(Item i) => i.ParentInventory?.Owner is Item ownerItem && ownerItem.HasTag("mobileradio");
ammunition = character.Inventory.FindItem(i => ammunitionIdentifiers.Any(id => id == i.Prefab.Identifier || i.HasTag(id)) && i.Condition > 0 && !IsInsideHeadset(i), recursive: true);
static bool IsInsideHeadset(Item i) => i.ParentInventory?.Owner is Item ownerItem && ownerItem.HasTag("mobileradio");
ammunition = character.Inventory.FindItem(i => CheckItemIdentifiersOrTags(i, ammunitionIdentifiers) && i.Condition > 0 && !IsInsideHeadset(i), recursive: true);
if (ammunition != null)
{
var container = Weapon.GetComponent<ItemContainer>();
@@ -1,6 +1,8 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
@@ -11,14 +13,14 @@ namespace Barotrauma
public Func<Item, float> GetItemPriority;
public Identifier[] ignoredContainerIdentifiers;
public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
public bool checkInventory = true;
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs and in some cases also enemy NPCs, like pirates)
private readonly bool spawnItemIfNotFound;
//can either be a tag or an identifier
public readonly Identifier[] itemIdentifiers;
public readonly ImmutableHashSet<Identifier> itemIdentifiers;
public readonly ItemContainer container;
private readonly Item item;
public Item ItemToContain { get; private set; }
@@ -61,9 +63,9 @@ namespace Barotrauma
}
public AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
: this(character, new Identifier[] { itemIdentifier }, container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
: this(character, itemIdentifier.ToEnumerable().ToImmutableHashSet(), container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
public AIObjectiveContainItem(Character character, Identifier[] itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
public AIObjectiveContainItem(Character character, ImmutableHashSet<Identifier> itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
: base(character, objectiveManager, priorityModifier)
{
this.itemIdentifiers = itemIdentifiers;
@@ -102,7 +104,10 @@ namespace Barotrauma
return containedItemCount >= ItemCount;
}
private bool CheckItem(Item i) => itemIdentifiers.Any(id => i.Prefab.Identifier == id || i.HasTag(id)) && i.ConditionPercentage >= ConditionLevel && i.HasAccess(character);
private bool CheckItem(Item item)
{
return CheckItemIdentifiersOrTags(item, itemIdentifiers) && item.ConditionPercentage >= ConditionLevel && item.HasAccess(character);
}
protected override void Act(float deltaTime)
{
@@ -1,5 +1,7 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System;
using System.Collections.Immutable;
using System.Linq;
namespace Barotrauma
@@ -13,7 +15,7 @@ namespace Barotrauma
//can either be a tag or an identifier
private readonly string[] itemIdentifiers;
private readonly ItemContainer sourceContainer;
private ItemContainer targetContainer;
private readonly ItemContainer targetContainer;
private readonly Item targetItem;
private AIObjectiveGetItem getItemObjective;
@@ -127,7 +129,7 @@ namespace Barotrauma
RemoveExistingPredicate = RemoveExistingPredicate,
RemoveMax = RemoveExistingMax,
GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = sourceContainer != null ? new Identifier[] { sourceContainer.Item.Prefab.Identifier } : null
ignoredContainerIdentifiers = sourceContainer?.Item.Prefab.Identifier.ToEnumerable().ToImmutableHashSet()
},
onCompleted: () => IsCompleted = true,
onAbandon: () => Abandon = true);
@@ -177,7 +177,7 @@ namespace Barotrauma
TargetName = Leak.FlowTargetHull?.DisplayName,
requiredCondition = () =>
Leak.Submarine == character.Submarine &&
Leak.linkedTo.Any(e => e is Hull h && character.CurrentHull == h),
Leak.linkedTo.Any(e => e is Hull h && (character.CurrentHull == h || h.linkedTo.Contains(character.CurrentHull))),
endNodeFilter = n => n.Waypoint.CurrentHull != null && Leak.linkedTo.Any(e => e is Hull h && h == n.Waypoint.CurrentHull),
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
@@ -21,10 +21,10 @@ namespace Barotrauma
public float TargetCondition { get; set; } = 1;
public bool AllowDangerousPressure { get; set; }
public readonly ImmutableArray<Identifier> IdentifiersOrTags;
public readonly ImmutableHashSet<Identifier> IdentifiersOrTags;
//if the item can't be found, spawn it in the character's inventory (used by outpost NPCs)
private bool spawnItemIfNotFound = false;
private readonly bool spawnItemIfNotFound = false;
private Item targetItem;
private readonly Item originalTarget;
@@ -32,8 +32,8 @@ namespace Barotrauma
private bool isDoneSeeking;
public Item TargetItem => targetItem;
private int currSearchIndex;
public Identifier[] ignoredContainerIdentifiers;
public Identifier[] ignoredIdentifiersOrTags;
public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
public ImmutableHashSet<Identifier> ignoredIdentifiersOrTags;
private AIObjectiveGoTo goToObjective;
private float currItemPriority;
private readonly bool checkInventory;
@@ -93,8 +93,8 @@ namespace Barotrauma
Equip = equip;
this.spawnItemIfNotFound = spawnItemIfNotFound;
this.checkInventory = checkInventory;
IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableArray();
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToArray();
IdentifiersOrTags = ParseGearTags(identifiersOrTags).ToImmutableHashSet();
ignoredIdentifiersOrTags = ParseIgnoredTags(identifiersOrTags).ToImmutableHashSet();
}
public static IEnumerable<Identifier> ParseGearTags(IEnumerable<Identifier> identifiersOrTags)
@@ -558,11 +558,11 @@ namespace Barotrauma
{
if (!item.HasAccess(character)) { return false; }
if (ignoredItems.Contains(item)) { return false; };
if (ignoredIdentifiersOrTags != null && ignoredIdentifiersOrTags.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
if (ignoredIdentifiersOrTags != null && CheckItemIdentifiersOrTags(item, ignoredIdentifiersOrTags)) { return false; }
if (item.Condition < TargetCondition) { return false; }
if (ItemFilter != null && !ItemFilter(item)) { return false; }
if (RequireLoaded && item.Components.Any(i => !i.IsLoaded(character))) { return false; }
return IdentifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && item.Prefab.VariantOf == id));
return CheckItemIdentifiersOrTags(item, IdentifiersOrTags) || (AllowVariants && !item.Prefab.VariantOf.IsEmpty && IdentifiersOrTags.Contains(item.Prefab.VariantOf));
}
public override void Reset()
@@ -25,7 +25,7 @@ namespace Barotrauma
public bool RequireAllItems { get; set; }
private readonly ImmutableArray<Identifier> gearTags;
private readonly Identifier[] ignoredTags;
private readonly ImmutableHashSet<Identifier> ignoredTags;
private bool subObjectivesCreated;
public readonly HashSet<Item> achievedItems = new HashSet<Item>();
@@ -33,7 +33,7 @@ namespace Barotrauma
public AIObjectiveGetItems(Character character, AIObjectiveManager objectiveManager, IEnumerable<Identifier> identifiersOrTags, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier)
{
gearTags = AIObjectiveGetItem.ParseGearTags(identifiersOrTags).ToImmutableArray();
ignoredTags = AIObjectiveGetItem.ParseIgnoredTags(identifiersOrTags).ToArray();
ignoredTags = AIObjectiveGetItem.ParseIgnoredTags(identifiersOrTags).ToImmutableHashSet();
}
protected override bool CheckObjectiveSpecific() => subObjectivesCreated && subObjectives.None();
@@ -196,9 +196,10 @@ namespace Barotrauma
character.AIController.SteeringManager.Reset();
return;
}
if (!character.IsClimbing)
character.SelectedItem = null;
if (character.SelectedSecondaryItem != null && !character.SelectedSecondaryItem.IsLadder)
{
character.SelectedConstruction = null;
character.SelectedSecondaryItem = null;
}
if (Target is Entity e)
{
@@ -594,6 +595,10 @@ namespace Barotrauma
{
return c.CurrentHull;
}
else if (target is Structure structure)
{
return Hull.FindHull(structure.Position, useWorldCoordinates: false);
}
else if (target is Gap g)
{
return g.FlowTargetHull;
@@ -647,7 +652,7 @@ namespace Barotrauma
{
if (character.IsClimbing)
{
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder)
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder && !PathSteering.CurrentPath.IsAtEndNode)
{
if (Target.WorldPosition.Y > character.WorldPosition.Y)
{
@@ -694,7 +699,7 @@ namespace Barotrauma
{
if (Target is Item item)
{
if (!character.IsClimbing && character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
if (character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
}
else if (Target is Character targetCharacter)
{
@@ -161,10 +161,7 @@ namespace Barotrauma
character.DeselectCharacter();
}
if (!character.IsClimbing)
{
character.SelectedConstruction = null;
}
character.SelectedItem = null;
CleanupItems(deltaTime);
@@ -262,7 +259,8 @@ namespace Barotrauma
// Check that there is no unsafe hulls on the way to the target
if (node.Waypoint.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(node.Waypoint.CurrentHull)) { return false; }
return true;
}, endNodeFilter: node => !isCurrentHullAllowed | !IsForbidden(node.Waypoint.CurrentHull));
//don't stop at ladders when idling
}, endNodeFilter: node => node.Waypoint.Ladders == null && (!isCurrentHullAllowed || !IsForbidden(node.Waypoint.CurrentHull)));
if (path.Unreachable)
{
//can't go to this room, remove it from the list and try another room
@@ -293,7 +291,9 @@ namespace Barotrauma
}
else if (currentTarget != null)
{
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1, nodeFilter: node => node.Waypoint.CurrentHull != null);
PathSteering.SteeringSeek(character.GetRelativeSimPosition(currentTarget), weight: 1,
nodeFilter: node => node.Waypoint.CurrentHull != null,
endNodeFilter: node => node.Waypoint.Ladders == null);
}
else
{
@@ -310,7 +310,7 @@ namespace Barotrauma
if (character.AnimController.GetHeightFromFloor() < 0.1f)
{
character.AnimController.Anim = AnimController.Animation.None;
character.SelectedConstruction = null;
character.SelectedSecondaryItem = null;
}
return;
}
@@ -375,7 +375,7 @@ namespace Barotrauma
}
chairCheckTimer -= deltaTime;
if (chairCheckTimer <= 0.0f && character.SelectedConstruction == null)
if (chairCheckTimer <= 0.0f && character.SelectedSecondaryItem == null)
{
foreach (Item item in Item.ItemList)
{
@@ -54,7 +54,7 @@ namespace Barotrauma
if (ValidContainableItemIdentifiers.None())
{
#if DEBUG
DebugConsole.ShowError($"No valid containable item identifiers found for the Load Item objective targeting {Container}");
DebugConsole.LogError($"No valid containable item identifiers found for the Load Item objective targeting {Container}");
#endif
Abandon = true;
return;
@@ -250,7 +250,7 @@ namespace Barotrauma
catch (NotImplementedException)
{
#if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
DebugConsole.LogError($"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
#endif
return 0.0f;
}
@@ -90,7 +90,7 @@ namespace Barotrauma
catch (NotImplementedException)
{
#if DEBUG
DebugConsole.ShowError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
DebugConsole.LogError($"Unexpected target condition \"{targetCondition}\" in AIObjectiveLoadItems.ItemMatchesTargetCondition");
#endif
return false;
}
@@ -37,6 +37,8 @@ namespace Barotrauma
public Func<bool> completionCondition;
private bool isDoneOperating;
public float? OverridePriority = null;
protected override float GetPriority()
{
bool isOrder = objectiveManager.IsOrder(this);
@@ -52,7 +54,11 @@ namespace Barotrauma
}
else
{
if (isOrder)
if (OverridePriority.HasValue)
{
Priority = OverridePriority.Value;
}
else if (isOrder)
{
Priority = objectiveManager.GetOrderPriority(this);
}
@@ -135,7 +141,7 @@ namespace Barotrauma
float value = CumulatedDevotion + (max * PriorityModifier);
Priority = MathHelper.Clamp(value, 0, max);
}
else
else if (!OverridePriority.HasValue)
{
float value = CumulatedDevotion + (AIObjectiveManager.LowestOrderPriority * PriorityModifier);
float max = AIObjectiveManager.LowestOrderPriority - 1;
@@ -204,8 +210,15 @@ namespace Barotrauma
{
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
{
HumanAIController.FaceTarget(target.Item);
if (character.SelectedConstruction != target.Item)
if (target.Item.GetComponent<Controller>() is not Controller { ControlCharacterPose: true })
{
HumanAIController.FaceTarget(target.Item);
}
else
{
HumanAIController.SteeringManager.Reset();
}
if (character.SelectedItem != target.Item && character.SelectedSecondaryItem != target.Item)
{
target.Item.TryInteract(character, forceSelectKey: true);
}
@@ -26,7 +26,7 @@ namespace Barotrauma
private bool IsRepairing() => IsRepairing(character, Item);
private readonly bool isPriority;
public static bool IsRepairing(Character character, Item item) => character.SelectedConstruction == item && item.Repairables.Any(r => r.CurrentFixer == character);
public static bool IsRepairing(Character character, Item item) => character.SelectedItem == item && item.Repairables.Any(r => r.CurrentFixer == character);
public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool isPriority = false)
: base(character, objectiveManager, priorityModifier)
@@ -165,7 +165,7 @@ namespace Barotrauma
return;
}
}
if (!character.IsClimbing && character.CanInteractWith(Item, out _, checkLinked: false))
if (character.CanInteractWith(Item, out _, checkLinked: false))
{
waitTimer += deltaTime;
if (waitTimer < WaitTimeBeforeRepair) { return; }
@@ -184,12 +184,12 @@ namespace Barotrauma
}
if (!Abandon)
{
if (character.SelectedConstruction != Item)
if (character.SelectedItem != Item)
{
if (Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) ||
Item.TryInteract(character, ignoreRequiredItems: true, forceUseKey: true))
{
character.SelectedConstruction = Item;
character.SelectedItem = Item;
}
else
{
@@ -232,8 +232,6 @@ namespace Barotrauma
previousCondition = -1;
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
{
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
endNodeFilter = node => node.Waypoint.Ladders == null,
TargetName = Item.Name
};
if (repairTool != null)
@@ -67,7 +67,7 @@ namespace Barotrauma
if (!ViableForRepair(item, character, HumanAIController)) { return false; };
if (!Objectives.ContainsKey(item))
{
if (item != character.SelectedConstruction)
if (item != character.SelectedItem)
{
if (NearlyFullCondition(item)) { return false; }
}
@@ -96,7 +96,7 @@ namespace Barotrauma
protected override float TargetEvaluation()
{
var selectedItem = character.SelectedConstruction;
var selectedItem = character.SelectedItem;
if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100)
{
// Don't stop fixing until completely done
@@ -278,7 +278,7 @@ namespace Barotrauma
float cprSuitability = targetCharacter.Oxygen < 0.0f ? -targetCharacter.Oxygen * 100.0f : 0.0f;
//find which treatments are the most suitable to treat the character's current condition
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, normalize: false, predictFutureDuration: 10.0f);
targetCharacter.CharacterHealth.GetSuitableTreatments(currentTreatmentSuitabilities, user: character, normalize: false, predictFutureDuration: 10.0f);
//check if we already have a suitable treatment for any of the afflictions
foreach (Affliction affliction in GetSortedAfflictions(targetCharacter))