Build 0.18.5.0

This commit is contained in:
Markus Isberg
2022-06-03 22:29:04 +09:00
parent 64db1a6a44
commit 6be757a45b
72 changed files with 869 additions and 439 deletions
@@ -2,7 +2,6 @@
using Barotrauma.Items.Components;
using System.Collections.Generic;
using System.Linq;
using System;
namespace Barotrauma
{
@@ -48,6 +47,9 @@ namespace Barotrauma
protected override bool Filter(Item target)
{
System.Diagnostics.Debug.Assert(target.GetComponent<Pickable>() is { } pickable && !pickable.IsAttached, "Invalid target in AIObjectiveCleanUpItems - the the objective should only be checking pickable, non-attached items.");
System.Diagnostics.Debug.Assert(target.Prefab.PreferredContainers.Any(), "Invalid target in AIObjectiveCleanUpItems - the the objective should only be checking items that have preferred containers defined.");
// 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, checkInventory: true)) { return Objectives.ContainsKey(target) && IsItemInsideValidSubmarine(target, character); }
@@ -57,7 +59,7 @@ namespace Barotrauma
return true;
}
protected override IEnumerable<Item> GetList() => Item.ItemList;
protected override IEnumerable<Item> GetList() => Item.CleanableItems;
protected override AIObjective ObjectiveConstructor(Item item)
=> new AIObjectiveCleanupItem(item, character, objectiveManager, priorityModifier: PriorityModifier)
@@ -102,9 +104,6 @@ namespace Barotrauma
}
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
if (item.HasBallastFloraInHull) { return false; }
var pickable = item.GetComponent<Pickable>();
if (pickable == null) { return false; }
if (pickable is Holdable h && h.Attachable && h.Attached) { return false; }
var wire = item.GetComponent<Wire>();
if (wire != null)
{
@@ -118,10 +117,6 @@ namespace Barotrauma
return false;
}
}
if (item.Prefab.PreferredContainers.None())
{
return false;
}
if (!checkInventory)
{
return true;
@@ -488,7 +488,7 @@ namespace Barotrauma
if (hull != null)
{
itemsToClean.Clear();
foreach (Item item in Item.ItemList)
foreach (Item item in Item.CleanableItems)
{
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveCleanupItems.IsValidTarget(item, character, checkInventory: true, allowUnloading: false) && !ignoredItems.Contains(item))
@@ -39,12 +39,21 @@ namespace Barotrauma
{
TargetContainers.Add(targetContainer);
}
else
{
foreach (Item item in Item.ItemList)
{
if (!OrderPrefab.TargetItemsMatchItem(TargetContainerTags, item)) { continue; }
TargetContainers.Add(item);
}
}
TargetCondition = option == "turretammo" ? ItemCondition.Empty : ItemCondition.Full;
}
protected override bool Filter(Item target)
{
if (!IsValidTarget(target, character, TargetContainerTags, TargetCondition)) { return false; }
//don't pass TargetContainerTags to the method (no need to filter by tags anymore, it's already done when populating TargetContainers)
if (!IsValidTarget(target, character, null, TargetCondition)) { return false; }
if (target.CurrentHull == null || target.CurrentHull.FireSources.Count > 0) { return false; }
if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
return true;
@@ -52,8 +61,7 @@ namespace Barotrauma
public static bool IsValidTarget(Item item, Character character, ImmutableArray<Identifier>? targetContainerTags = null, ItemCondition? targetCondition = null)
{
if (item == null) { return false; }
if (item.Removed) { return false; }
if (item == null || item.Removed) { return false; }
if (targetContainerTags.HasValue && !OrderPrefab.TargetItemsMatchItem(targetContainerTags.Value, item)) { return false; }
if (!(item.GetComponent<ItemContainer>() is ItemContainer container)) { return false; }
if (container.Inventory == null) { return false; }
@@ -88,7 +96,7 @@ namespace Barotrauma
}
}
protected override IEnumerable<Item> GetList() => TargetContainers.Any() ? TargetContainers : Item.ItemList;
protected override IEnumerable<Item> GetList() => TargetContainers;
protected override AIObjective ObjectiveConstructor(Item target)
=> new AIObjectiveLoadItem(target, TargetContainerTags, TargetCondition, Option, character, objectiveManager, PriorityModifier);
@@ -13,7 +13,7 @@ namespace Barotrauma
public override bool KeepDivingGearOn => true;
public override bool AllowAutomaticItemUnequipping => true;
private IEnumerable<Pump> pumpList;
private List<Pump> pumpList;
public AIObjectivePumpWater(Character character, AIObjectiveManager objectiveManager, Identifier option, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier, option) { }
@@ -26,13 +26,8 @@ namespace Barotrauma
protected override bool Filter(Pump pump)
{
if (pump == null) { return false; }
if (pump.Item.IgnoreByAI(character)) { return false; }
if (!pump.Item.IsInteractable(character)) { return false; }
if (pump.Item.HasTag("ballast")) { return false; }
if (pump.Item.Submarine == null) { return false; }
if (pump.Item.CurrentHull == null) { return false; }
if (pump.Item.Submarine.TeamID != character.TeamID) { return false; }
if (pump.IsAutoControlled) { return false; }
if (pump.Item.ConditionPercentage <= 0) { return false; }
if (pump.Item.CurrentHull.FireSources.Count > 0) { return false; }
@@ -50,7 +45,16 @@ namespace Barotrauma
if (pumpList == null)
{
if (character == null || character.Submarine == null) { return Array.Empty<Pump>(); }
pumpList = character.Submarine.GetItems(true).Select(i => i.GetComponent<Pump>()).Where(p => p != null);
pumpList = new List<Pump>();
foreach (Item item in character.Submarine.GetItems(true))
{
var pump = item.GetComponent<Pump>();
if (pump == null || pump.Item.Submarine == null || pump.Item.CurrentHull == null) { continue; }
if (pump.Item.Submarine.TeamID != character.TeamID) { continue; }
if (pump.Item.HasTag("ballast")) { continue; }
pumpList.Add(pump);
}
}
return pumpList;
}
@@ -136,7 +136,7 @@ namespace Barotrauma
return MathHelper.Lerp(0, 100, MathHelper.Clamp(damagePriority * successFactor, 0, 1));
}
protected override IEnumerable<Item> GetList() => Item.ItemList;
protected override IEnumerable<Item> GetList() => Item.RepairableItems;
protected override AIObjective ObjectiveConstructor(Item item)
=> new AIObjectiveRepairItem(character, item, objectiveManager, priorityModifier: PriorityModifier, isPriority: item == PrioritizedItem);
@@ -156,6 +156,9 @@ namespace Barotrauma
if (character.IsOnPlayerTeam && item.Submarine.Info.IsOutpost) { return false; }
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return false; }
if (item.Repairables.None()) { return false; }
System.Diagnostics.Debug.Assert(item.Repairables.Any(), "Invalid target in AIObjectiveRepairItems - the objective should only be checking items that have a Repairable component (Item.RepairableItems)");
return true;
}
}
@@ -356,7 +356,10 @@ namespace Barotrauma
}
}
public bool OmitJobInPortraitClothing;
/// <summary>
/// Can be used to disable displaying the job in any info panels
/// </summary>
public bool OmitJobInMenus;
private Sprite portrait;
public Sprite Portrait
@@ -434,7 +437,7 @@ namespace Barotrauma
{
if (attachmentSprites == null)
{
LoadAttachmentSprites(OmitJobInPortraitClothing);
LoadAttachmentSprites();
}
return attachmentSprites;
}
@@ -1092,7 +1095,7 @@ namespace Barotrauma
private static IEnumerable<float> GetWeights(IEnumerable<ContentXElement> elements) => elements.Select(h => h.GetAttributeFloat("commonness", 1f));
partial void LoadAttachmentSprites(bool omitJob);
partial void LoadAttachmentSprites();
private int CalculateSalary()
{
@@ -79,7 +79,6 @@ namespace Barotrauma
/// </summary>
public static IReadOnlyDictionary<Identifier, float> ItemRepairPriorities => _itemRepairPriorities;
public static ContentXElement NoJobElement;
public static JobPrefab Get(string identifier)
{
if (Prefabs.ContainsKey(identifier))
@@ -213,7 +212,7 @@ namespace Barotrauma
public SkillPrefab PrimarySkill => Skills?.FirstOrDefault(s => s.IsPrimarySkill);
public ContentXElement Element { get; private set; }
public ContentXElement ClothingElement { get; private set; }
public int Variants { get; private set; }
public JobPrefab(ContentXElement element, JobsFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
@@ -288,9 +287,6 @@ namespace Barotrauma
Variants = variant;
Skills.Sort((x,y) => y.LevelRange.Start.CompareTo(x.LevelRange.Start));
// Disabled on purpose, TODO: remove all references?
//ClothingElement = element.GetChildElement("PortraitClothing");
}
public static JobPrefab Random(Rand.RandSync sync, Func<JobPrefab, bool> predicate = null) => Prefabs.GetRandom(p => !p.HiddenJob && (predicate == null || predicate(p)), sync);
@@ -121,7 +121,7 @@ namespace Barotrauma
return folder.CleanUpPathCrossPlatform(correctFilenameCase: true);
}
public static T GetDefaultRagdollParams<T>(Identifier speciesName) where T : RagdollParams, new() => GetRagdollParams<T>(speciesName, GetDefaultFileName(speciesName));
public static T GetDefaultRagdollParams<T>(Identifier speciesName) where T : RagdollParams, new() => GetRagdollParams<T>(speciesName);
/// <summary>
/// If the file name is left null, default file is selected. If fails, will select the default file. Note: Use the filename without the extensions, don't use the full path!
@@ -22,11 +22,7 @@ namespace Barotrauma
{
foreach (var element in mainElement.Elements())
{
if (element.NameAsIdentifier() == "nojob")
{
JobPrefab.NoJobElement ??= element;
}
else if (element.NameAsIdentifier() == "ItemRepairPriorities")
if (element.NameAsIdentifier() == "ItemRepairPriorities")
{
foreach (var subElement in element.Elements())
{
@@ -177,4 +177,11 @@ namespace Barotrauma
Int,
Float
}
public enum ChatMode
{
None,
Local,
Radio
}
}
@@ -1028,13 +1028,14 @@ namespace Barotrauma
var itemsToTransfer = new List<(Item item, Item container)>();
if (PendingSubmarineSwitch != null)
{
var connectedSubs = currentSub.GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet();
// Remove items from the old sub
foreach (Item item in Item.ItemList)
{
if (item.Removed) { continue; }
if (item.NonInteractable) { continue; }
if (item.HiddenInGame) { continue; }
if (item.Submarine != currentSub) { continue; }
if (!connectedSubs.Contains(item.Submarine)) { continue; }
if (item.Prefab.DontTransferBetweenSubs) { continue; }
if (item.GetRootInventoryOwner() is Character) { continue; }
if (item.GetComponent<Holdable>() == null && item.GetComponent<Wearable>() == null && item.GetComponent<Projectile>() == null) { continue; }
@@ -1058,9 +1059,10 @@ namespace Barotrauma
{
// Load the new sub
var newSub = new Submarine(PendingSubmarineSwitch);
var connectedSubs = newSub.GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet();
// Move the transferred items
List<ItemContainer> availableContainers = Item.ItemList
.Where(it => it.Submarine == newSub && it.HasTag("crate") && !it.NonInteractable && !it.HiddenInGame && !it.Removed)
.Where(it => connectedSubs.Contains(it.Submarine) && it.HasTag("crate") && !it.NonInteractable && !it.HiddenInGame && !it.Removed)
.Select(it => it.GetComponent<ItemContainer>())
.Where(c => c != null)
.ToList();
@@ -1070,7 +1072,7 @@ namespace Barotrauma
item.Submarine = newSub;
if (item.Container == null)
{
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true);
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true, allowConnectedSubs: true);
}
if (item.Container == null && (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null, createNetworkEvent: false)))
{
@@ -1086,7 +1088,8 @@ namespace Barotrauma
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, spawnHull, ref availableContainers);
if (cargoContainer == null || !cargoContainer.Inventory.TryPutItem(item, user: null, createNetworkEvent: false))
{
item.SetTransform(wp.SimPosition, 0.0f, findNewHull: false, setPrevTransform: false);
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
item.SetTransform(simPos, 0.0f, findNewHull: false, setPrevTransform: false);
}
}
else
@@ -71,12 +71,22 @@ namespace Barotrauma
public float GetFloat(Identifier identifier)
{
return values.TryGetValue(identifier, out Either<int, float> value) && value.TryGet(out float range) ? range : 0.0f;
float range = 0;
if (!values.TryGetValue(identifier, out Either<int, float> value) || !value.TryGet(out range))
{
DebugConsole.ThrowError($"CampaignSettings: Can't find value for {identifier}");
}
return range;
}
public int GetInt(Identifier identifier)
{
return values.TryGetValue(identifier, out Either<int, float> value) && value.TryGet(out int integer) ? integer : 0;
int integer = 0;
if (!values.TryGetValue(identifier, out Either<int, float> value) || !value.TryGet(out integer))
{
DebugConsole.ThrowError($"CampaignSettings: Can't find value for {identifier}");
}
return integer;
}
}
}
@@ -13,13 +13,14 @@ namespace Barotrauma
SelectNextCharacter,
SelectPreviousCharacter,
Voice,
LocalVoice,
Deselect,
Shoot,
Command,
TakeOneFromInventorySlot,
TakeHalfFromInventorySlot,
NextFireMode,
PreviousFireMode
PreviousFireMode,
ActiveChat,
ToggleChatMode,
}
}
@@ -270,7 +270,11 @@ namespace Barotrauma.Items.Components
{
Body?.SetTransform(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
}
if (linkedGap != null)
{
RefreshLinkedGap();
linkedGap.Rect = item.Rect;
}
#if CLIENT
UpdateConvexHulls();
#endif
@@ -41,6 +41,8 @@ namespace Barotrauma.Items.Components
private Character prevEquipper;
public override bool IsAttached => Attached;
private bool attachable, attached, attachedByDefault;
private Voronoi2.VoronoiCell attachTargetCell;
private PhysicsBody body;
@@ -71,6 +73,7 @@ namespace Barotrauma.Items.Components
set
{
attached = value;
item.CheckCleanable();
item.SetActiveSprite();
}
}
@@ -4,7 +4,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -20,6 +19,8 @@ namespace Barotrauma.Items.Components
private CoroutineHandle pickingCoroutine;
public virtual bool IsAttached => false;
public List<InvSlotType> AllowedSlots
{
get { return allowedSlots; }
@@ -94,7 +94,7 @@ namespace Barotrauma.Items.Components
get; set;
}
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit doors.")]
public bool HitItems { get; set; }
[Serialize(false, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
@@ -187,7 +187,7 @@ namespace Barotrauma.Items.Components
[Serialize(false, IsPropertySaveable.No)]
public bool RemoveContainedItemsOnDeconstruct { get; set; }
private readonly ImmutableArray<SlotRestrictions> slotRestrictions;
private ImmutableArray<SlotRestrictions> slotRestrictions;
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
@@ -215,13 +215,21 @@ namespace Barotrauma.Items.Components
public override bool RecreateGUIOnResolutionChange => true;
public List<RelatedItem> ContainableItems { get; }
public List<RelatedItem> ContainableItems { get; private set; }
public ItemContainer(Item item, ContentXElement element)
: base(item, element)
{
LoadContainableRestrictions(element);
InitProjSpecific(element);
}
public void LoadContainableRestrictions(ContentXElement element)
{
int totalCapacity = capacity;
ContainableItems?.Clear();
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -242,7 +250,7 @@ namespace Barotrauma.Items.Components
}
}
Inventory = new ItemInventory(item, this, totalCapacity, SlotsPerRow);
List<SlotRestrictions> newSlotRestrictions = new List<SlotRestrictions>(totalCapacity);
for (int i = 0; i < capacity; i++)
{
@@ -253,7 +261,7 @@ namespace Barotrauma.Items.Components
foreach (var subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "subcontainer") { continue; }
int subCapacity = subElement.GetAttributeInt("capacity", 1);
int subMaxStackSize = subElement.GetAttributeInt("maxstacksize", maxStackSize);
@@ -281,7 +289,6 @@ namespace Barotrauma.Items.Components
capacity = totalCapacity;
slotRestrictions = newSlotRestrictions.ToImmutableArray();
System.Diagnostics.Debug.Assert(totalCapacity == slotRestrictions.Length);
InitProjSpecific(element);
}
public int GetMaxStackSize(int slotIndex)
@@ -114,6 +114,20 @@ namespace Barotrauma.Items.Components
private set;
} = true;
[Serialize(false, IsPropertySaveable.No)]
public bool NonInteractableWhenFlippedX
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No)]
public bool NonInteractableWhenFlippedY
{
get;
set;
}
public Controller(Item item, ContentXElement element)
: base(item, element)
{
@@ -571,6 +585,18 @@ namespace Barotrauma.Items.Components
}
}
public override void OnItemLoaded()
{
if (item.FlippedX && NonInteractableWhenFlippedX)
{
item.NonInteractable = true;
}
else if (item.FlippedY && NonInteractableWhenFlippedY)
{
item.NonInteractable = true;
}
}
public override void Reset()
{
base.Reset();
@@ -435,7 +435,7 @@ namespace Barotrauma.Items.Components
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("GridUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Power", sw.ElapsedTicks);
sw.Restart();
#endif
@@ -592,7 +592,7 @@ namespace Barotrauma.Items.Components
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("PowerUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Power", sw.ElapsedTicks);
#endif
}
@@ -17,7 +17,6 @@ namespace Barotrauma
Beard,
Moustache,
FaceAttachment,
JobIndicator,
Husk,
Herpes
}
@@ -128,7 +127,6 @@ namespace Barotrauma
case WearableType.Beard:
case WearableType.Moustache:
case WearableType.FaceAttachment:
case WearableType.JobIndicator:
case WearableType.Husk:
case WearableType.Herpes:
Limb = LimbType.Head;
@@ -29,6 +29,20 @@ namespace Barotrauma
public static IReadOnlyCollection<Item> DangerousItems { get { return dangerousItems; } }
private static readonly List<Item> repairableItems = new List<Item>();
/// <summary>
/// Items that have one more more Repairable component
/// </summary>
public static IReadOnlyCollection<Item> RepairableItems => repairableItems;
private static readonly List<Item> cleanableItems = new List<Item>();
/// <summary>
/// Items that may potentially need to be cleaned up (pickable, not attached to a wall, and not inside a valid container)
/// </summary>
public static IReadOnlyCollection<Item> CleanableItems => cleanableItems;
public new ItemPrefab Prefab => base.Prefab as ItemPrefab;
public static bool ShowLinks = true;
@@ -186,6 +200,7 @@ namespace Barotrauma
if (value != container)
{
container = value;
CheckCleanable();
SetActiveSprite();
}
}
@@ -1009,10 +1024,9 @@ namespace Barotrauma
InsertToList();
ItemList.Add(this);
if (Prefab.IsDangerous)
{
dangerousItems.Add(this);
}
if (Prefab.IsDangerous) { dangerousItems.Add(this); }
if (Repairables.Any()) { repairableItems.Add(this); }
CheckCleanable();
DebugConsole.Log("Created " + Name + " (" + ID + ")");
@@ -1022,6 +1036,9 @@ namespace Barotrauma
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
Components.ForEach(c => c.ApplyStatusEffects(ActionType.OnSpawn, 1.0f));
RecalculateConditionValues();
#if CLIENT
Submarine.ForceVisibilityRecheck();
#endif
}
partial void InitProjSpecific();
@@ -1150,6 +1167,7 @@ namespace Barotrauma
drawableComponents.Add(drawable);
hasComponentsToDraw = true;
#if CLIENT
Submarine.ForceVisibilityRecheck();
cachedVisibleExtents = null;
#endif
}
@@ -1281,6 +1299,27 @@ namespace Barotrauma
partial void SetActiveSpriteProjSpecific();
/// <summary>
/// Recheck if the item needs to be included in the list of cleanable items
/// </summary>
public void CheckCleanable()
{
var pickable = GetComponent<Pickable>();
if (pickable != null && !pickable.IsAttached &&
Prefab.PreferredContainers.Any() &&
(container == null || container.HasTag("allowcleanup")))
{
if (!cleanableItems.Contains(this))
{
cleanableItems.Add(this);
}
}
else
{
cleanableItems.Remove(this);
}
}
public override void Move(Vector2 amount, bool ignoreContacts = false)
{
if (!MathUtils.IsValid(amount))
@@ -2526,7 +2565,7 @@ namespace Barotrauma
ic.WasUsed = true;
#if CLIENT
ic.PlaySound(ActionType.OnUse, character);
#endif
#endif
ic.ApplyStatusEffects(ActionType.OnUse, deltaTime, character, targetLimb);
if (ic.DeleteOnUse) { remove = true; }
@@ -2704,6 +2743,9 @@ namespace Barotrauma
}
SetContainedItemPositions();
#if CLIENT
Submarine.ForceVisibilityRecheck();
#endif
}
public void Equip(Character character)
@@ -3368,8 +3410,7 @@ namespace Barotrauma
{
ic.ShallowRemove();
}
ItemList.Remove(this);
dangerousItems.Remove(this);
RemoveFromLists();
if (body != null)
{
@@ -3427,8 +3468,8 @@ namespace Barotrauma
ic.GuiFrame = null;
#endif
}
ItemList.Remove(this);
dangerousItems.Remove(this);
RemoveFromLists();
if (body != null)
{
@@ -3461,6 +3502,14 @@ namespace Barotrauma
RemoveProjSpecific();
}
private void RemoveFromLists()
{
ItemList.Remove(this);
dangerousItems.Remove(this);
repairableItems.Remove(this);
cleanableItems.Remove(this);
}
partial void RemoveProjSpecific();
public static void RemoveByPrefab(ItemPrefab prefab)
@@ -463,6 +463,15 @@ namespace Barotrauma.MapCreatures.Behavior
}
}
if (root == null)
{
Branches.ForEach(b => b.DisconnectedFromRoot = true);
}
else
{
CheckDisconnectedFromRoot();
}
void LoadBranch(XElement branchElement, IdRemap idRemap)
{
Vector2 pos = branchElement.GetAttributeVector2("pos", Vector2.Zero);
@@ -649,9 +658,10 @@ namespace Barotrauma.MapCreatures.Behavior
toBeRemoved.Clear();
foreach (BallastFloraBranch branch in Branches)
{
if (branch.ParentBranch != null && (branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f))
if (branch.ParentBranch == null || branch.ParentBranch.DisconnectedFromRoot || branch.ParentBranch.Health <= 0.0f)
{
float speed = MathHelper.Lerp(5.0f, 0.1f, branch.ParentBranch.Health / branch.ParentBranch.MaxHealth);
float parentHealth = branch.ParentBranch == null ? 0.0f : branch.ParentBranch.Health / branch.ParentBranch.MaxHealth;
float speed = MathHelper.Lerp(5.0f, 0.1f, parentHealth);
DamageBranch(branch, speed * speed * deltaTime, AttackType.CutFromRoot);
}
if (branch.Health <= 0.0f)
@@ -1071,6 +1081,25 @@ namespace Barotrauma.MapCreatures.Behavior
}
}
private void CheckDisconnectedFromRoot()
{
bool foundDisconnected;
do
{
foundDisconnected = false;
foreach (BallastFloraBranch branch in Branches)
{
if (branch.ParentBranch == null || branch.DisconnectedFromRoot) { continue; }
if (branch.ParentBranch.Removed || branch.ParentBranch.DisconnectedFromRoot)
{
branch.DisconnectedFromRoot = true;
foundDisconnected = true;
}
}
} while (foundDisconnected);
}
public void RemoveBranch(BallastFloraBranch branch)
{
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
@@ -1081,20 +1110,7 @@ namespace Barotrauma.MapCreatures.Behavior
Branches.Remove(branch);
branch.Removed = true;
bool foundDisconnected = false;
do
{
foundDisconnected = false;
foreach (BallastFloraBranch otherBranch in Branches)
{
if (otherBranch.ParentBranch == null || otherBranch.DisconnectedFromRoot) { continue; }
if (otherBranch.ParentBranch.Removed || otherBranch.ParentBranch.DisconnectedFromRoot)
{
otherBranch.DisconnectedFromRoot = true;
foundDisconnected = true;
}
}
} while (foundDisconnected);
CheckDisconnectedFromRoot();
bodies.ForEachMod(body =>
{
@@ -4056,7 +4056,11 @@ namespace Barotrauma
string beaconStationName = System.IO.Path.GetFileNameWithoutExtension(contentFile.Path.Value);
BeaconStation = SpawnSubOnPath(beaconStationName, contentFile, SubmarineType.BeaconStation);
if (BeaconStation == null) { return; }
if (BeaconStation == null)
{
LevelData.HasBeaconStation = false;
return;
}
Item sonarItem = Item.ItemList.Find(it => it.Submarine == BeaconStation && it.GetComponent<Sonar>() != null);
if (sonarItem == null)
@@ -4072,6 +4076,11 @@ namespace Barotrauma
if (!LevelData.HasBeaconStation) { return; }
if (GameMain.NetworkMember?.IsClient ?? false) { return; }
if (BeaconStation == null)
{
throw new InvalidOperationException("Failed to prepare beacon station (no beacon station in the level).");
}
List<Item> beaconItems = Item.ItemList.FindAll(it => it.Submarine == BeaconStation);
Item reactorItem = beaconItems.Find(it => it.GetComponent<Reactor>() != null);
@@ -567,6 +567,10 @@ namespace Barotrauma
/// </summary>
public static void UpdateAll(float deltaTime, Camera cam)
{
#if CLIENT
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
#endif
foreach (Hull hull in Hull.HullList)
{
hull.Update(deltaTime, cam);
@@ -594,6 +598,11 @@ namespace Barotrauma
gapUpdateTimer = 0;
}
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Update:MapEntity:Misc", sw.ElapsedTicks);
sw.Restart();
#endif
Powered.UpdatePower(deltaTime);
foreach (Item item in Item.ItemList)
{
@@ -602,6 +611,11 @@ namespace Barotrauma
UpdateAllProjSpecific(deltaTime);
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Update:MapEntity:Items", sw.ElapsedTicks);
sw.Restart();
#endif
Spawner?.Update();
}
@@ -1857,15 +1857,23 @@ namespace Barotrauma
public void RefreshOutdoorNodes() => OutdoorNodes.ForEach(n => n?.Waypoint?.FindHull());
public Item FindContainerFor(Item item, bool onlyPrimary, bool checkTransferConditions = false)
public Item FindContainerFor(Item item, bool onlyPrimary, bool checkTransferConditions = false, bool allowConnectedSubs = false)
{
var potentialContainers = new List<Item>();
var connectedSubs = GetConnectedSubs().Where(s => s.Info.Type == SubmarineType.Player).ToHashSet();
Item selectedContainer = null;
foreach (Item potentialContainer in Item.ItemList)
{
if (potentialContainer.Removed) { continue; }
if (potentialContainer.NonInteractable) { continue; }
if (potentialContainer.HiddenInGame) { continue; }
if (potentialContainer.Submarine != this) { continue; }
if (allowConnectedSubs)
{
if (!connectedSubs.Contains(potentialContainer.Submarine)) { continue; }
}
else
{
if (potentialContainer.Submarine != this) { continue; }
}
if (potentialContainer == item) { continue; }
if (potentialContainer.Condition <= 0) { continue; }
if (potentialContainer.OwnInventory == null) { continue; }
@@ -1875,13 +1883,15 @@ namespace Barotrauma
if (!potentialContainer.OwnInventory.CanBePut(item)) { continue; }
if (!container.ShouldBeContained(item, out _)) { continue; }
if (!item.Prefab.IsContainerPreferred(item, container, out bool isPreferencesDefined, out bool isSecondary, checkTransferConditions: checkTransferConditions) || !isPreferencesDefined || onlyPrimary && isSecondary) { continue; }
potentialContainers.Add(potentialContainer);
if (!isSecondary)
if (potentialContainer.Submarine == this && !isSecondary)
{
break;
//valid primary container in the same sub -> perfect, let's use that one
return potentialContainer;
}
selectedContainer = potentialContainer;
}
return potentialContainers.LastOrDefault();
return selectedContainer;
}
}
}
@@ -117,6 +117,8 @@ namespace Barotrauma.Networking
set;
}
public ChatMode ChatMode { get; set; } = ChatMode.None;
protected ChatMessage(string senderName, string text, ChatMessageType type, Character sender, Client client, PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None, Color? textColor = null)
{
Text = text;
@@ -23,7 +23,6 @@ namespace Barotrauma
private readonly Dictionary<string, Queue<long>> elapsedTicks = new Dictionary<string, Queue<long>>();
private readonly Dictionary<string, long> avgTicksPerFrame = new Dictionary<string, long>();
private readonly Dictionary<string, Dictionary<string, TickInfo>> partialTickInfos = new Dictionary<string, Dictionary<string, TickInfo>>();
#if CLIENT
internal Graph UpdateTimeGraph = new Graph(500), DrawTimeGraph = new Graph(500);
@@ -43,20 +42,6 @@ namespace Barotrauma
}
}
private readonly List<string> tempSavedPartialIdentifiers = new List<string>();
public IReadOnlyList<string> GetSavedPartialIdentifiers(string parentIdentifier)
{
lock (mutex)
{
tempSavedPartialIdentifiers.Clear();
if (partialTickInfos.TryGetValue(parentIdentifier, out var tickInfos))
{
tempSavedPartialIdentifiers.AddRange(tickInfos.Keys);
}
}
return tempSavedPartialIdentifiers;
}
public void AddElapsedTicks(string identifier, long ticks)
{
lock (mutex)
@@ -72,29 +57,6 @@ namespace Barotrauma
}
}
public void AddPartialElapsedTicks(string parentIdentifier, string identifier, long ticks)
{
lock (mutex)
{
if (!partialTickInfos.TryGetValue(parentIdentifier, out var tickInfos))
{
tickInfos = new Dictionary<string, TickInfo>();
partialTickInfos.Add(parentIdentifier, tickInfos);
}
if (!tickInfos.TryGetValue(identifier, out var tickInfo))
{
tickInfo = new TickInfo();
tickInfos.Add(identifier, tickInfo);
}
tickInfo.ElapsedTicks.Enqueue(ticks);
if (tickInfo.ElapsedTicks.Count > MaximumSamples)
{
tickInfo.ElapsedTicks.Dequeue();
tickInfo.AvgTicksPerFrame = (long)tickInfo.ElapsedTicks.Average(i => i);
}
}
}
public float GetAverageElapsedMillisecs(string identifier)
{
long ticksPerFrame = 0;
@@ -105,18 +67,6 @@ namespace Barotrauma
return ticksPerFrame * 1000.0f / Stopwatch.Frequency;
}
public float GetPartialAverageElapsedMillisecs(string parentIdentifier, string identifier)
{
long ticksPerFrame = 0;
lock (mutex)
{
if (!partialTickInfos.TryGetValue(parentIdentifier, out var tickInfos)) { return 0.0f; }
if (!tickInfos.TryGetValue(identifier, out var tickInfo)) { return 0.0f; }
ticksPerFrame = tickInfo.AvgTicksPerFrame;
}
return ticksPerFrame * 1000.0f / Stopwatch.Frequency;
}
public bool Update(double deltaTime)
{
if (deltaTime == 0.0f) { return false; }
@@ -149,19 +149,19 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("GameSessionUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:GameSession", sw.ElapsedTicks);
sw.Restart();
GameMain.ParticleManager.Update((float)deltaTime);
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("ParticleUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Particles", sw.ElapsedTicks);
sw.Restart();
if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime, cam);
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("LevelUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Level", sw.ElapsedTicks);
if (Character.Controlled != null)
{
@@ -193,7 +193,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("CharacterUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Character", sw.ElapsedTicks);
sw.Restart();
#endif
@@ -201,7 +201,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("StatusEffectUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:StatusEffects", sw.ElapsedTicks);
sw.Restart();
if (Character.Controlled != null &&
@@ -253,7 +253,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("MapEntityUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:MapEntity", sw.ElapsedTicks);
sw.Restart();
#endif
Character.UpdateAnimAll((float)deltaTime);
@@ -266,7 +266,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("AnimUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Ragdolls", sw.ElapsedTicks);
sw.Restart();
#endif
@@ -277,7 +277,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("SubmarineUpdate", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Submarine", sw.ElapsedTicks);
sw.Restart();
#endif
@@ -297,7 +297,7 @@ namespace Barotrauma
#if CLIENT
sw.Stop();
GameMain.PerformanceCounter.AddElapsedTicks("Physics", sw.ElapsedTicks);
GameMain.PerformanceCounter.AddElapsedTicks("Update:Physics", sw.ElapsedTicks);
#endif
UpdateProjSpecific(deltaTime);
@@ -1119,7 +1119,12 @@ namespace Barotrauma
itemComponent.SetRequiredItems(element, allowEmpty: true);
break;
}
}
}
if (itemComponent is ItemContainer itemContainer &&
(componentElement.GetChildElement("containable") != null || componentElement.GetChildElement("subcontainer") != null))
{
itemContainer.LoadContainableRestrictions(componentElement);
}
}
}
}
@@ -226,6 +226,7 @@ namespace Barotrauma
{
MusicVolume = 0.3f,
SoundVolume = 0.5f,
UiVolume = 0.3f,
VoiceChatVolume = 0.5f,
VoiceChatCutoffPrevention = 0,
MicrophoneVolume = 5,
@@ -234,7 +235,6 @@ namespace Barotrauma
UseDirectionalVoiceChat = true,
VoipAttenuationEnabled = true,
VoiceSetting = VoiceMode.PushToTalk,
UseLocalVoiceByDefault = false,
DisableVoiceChatFilters = false
};
return audioSettings;
@@ -249,6 +249,7 @@ namespace Barotrauma
public float MusicVolume;
public float SoundVolume;
public float UiVolume;
public float VoiceChatVolume;
public int VoiceChatCutoffPrevention;
public float MicrophoneVolume;
@@ -264,7 +265,6 @@ namespace Barotrauma
public string VoiceCaptureDevice;
public float NoiseGateThreshold;
public bool UseLocalVoiceByDefault;
public bool DisableVoiceChatFilters;
}
@@ -286,12 +286,13 @@ namespace Barotrauma
{ InputType.Aim, MouseButton.SecondaryMouse },
{ InputType.InfoTab, Keys.Tab },
{ InputType.Chat, Keys.T },
{ InputType.RadioChat, Keys.R },
{ InputType.Chat, Keys.None },
{ InputType.RadioChat, Keys.None },
{ InputType.ActiveChat, Keys.T },
{ InputType.CrewOrders, Keys.C },
{ InputType.Voice, Keys.V },
{ InputType.LocalVoice, Keys.B },
{ InputType.ToggleChatMode, Keys.R },
{ InputType.Command, MouseButton.MiddleMouse },
{ InputType.PreviousFireMode, MouseButton.MouseWheelDown },
{ InputType.NextFireMode, MouseButton.MouseWheelUp },
@@ -332,17 +333,35 @@ namespace Barotrauma
if (!bindings.ContainsKey(inputType)) { bindings.Add(inputType, defaultBindings[inputType]); }
}
bool playerConfigContainsNewChatBinds = false;
foreach (XElement element in elements)
{
foreach (XAttribute attribute in element.Attributes())
{
if (Enum.TryParse(attribute.Name.LocalName, out InputType result))
{
if (!playerConfigContainsNewChatBinds)
{
playerConfigContainsNewChatBinds = result == InputType.ActiveChat;
}
bindings[result] = element.GetAttributeKeyOrMouse(attribute.Name.LocalName, bindings[result]);
}
}
}
// Clear the old chat binds for configs saved before the introduction of the new chat binds
if (!playerConfigContainsNewChatBinds)
{
if (bindings.ContainsKey(InputType.Chat))
{
bindings[InputType.Chat] = Keys.None;
}
if (bindings.ContainsKey(InputType.RadioChat))
{
bindings[InputType.RadioChat] = Keys.None;
}
}
Bindings = bindings.ToImmutableDictionary();
}
@@ -157,7 +157,7 @@ namespace Barotrauma
/// Should the item spawn even if the container can't contain items of this type
/// </summary>
public readonly bool SpawnIfCantBeContained;
public readonly float Speed;
public readonly float Impulse;
public readonly float Rotation;
public readonly int Count;
public readonly float Spread;
@@ -198,7 +198,7 @@ namespace Barotrauma
SpawnIfInventoryFull = element.GetAttributeBool("spawnifinventoryfull", false);
SpawnIfCantBeContained = element.GetAttributeBool("spawnifcantbecontained", true);
Speed = element.GetAttributeFloat("speed", 0.0f);
Impulse = element.GetAttributeFloat("impulse", element.GetAttributeFloat("speed", 0.0f));
Condition = MathHelper.Clamp(element.GetAttributeFloat("condition", 1.0f), 0.0f, 1.0f);
@@ -1708,7 +1708,7 @@ namespace Barotrauma
throw new NotImplementedException("Spawn rotation type not implemented: " + chosenItemSpawnInfo.RotationType);
}
body.SetTransform(newItem.SimPosition, rotation);
body.ApplyLinearImpulse(Rand.Vector(1) * chosenItemSpawnInfo.Speed);
body.ApplyLinearImpulse(Rand.Vector(1) * chosenItemSpawnInfo.Impulse);
}
}
newItem.Condition = newItem.MaxCondition * chosenItemSpawnInfo.Condition;