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,8 +2,8 @@
<CampaignSettingPresets>
<CampaignSettingDefinitions>
<StartingBalanceAmount high="10000" medium="8500" low="6000" />
<ExtraEventManagerDifficulty easy="-15.0" normal="0.0" hard="20.0" hellish="60.0" />
<LevelDifficultyMultiplier easy="0.6" normal="1.0" hard="1.6" hellish="10.0" />
<ExtraEventManagerDifficulty easy="-15.0" medium="0.0" hard="20.0" hellish="60.0" />
<LevelDifficultyMultiplier easy="0.6" medium="1.0" hard="1.6" hellish="10.0" />
</CampaignSettingDefinitions>
<CampaignSettings
presetname="Easy"
@@ -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;
+42 -2
View File
@@ -1,3 +1,38 @@
---------------------------------------------------------------------------------------------------------
v0.18.5.0
---------------------------------------------------------------------------------------------------------
Chat improvements:
- Chat mode (radio/local) can be toggled using a dropdown next to the chat box or with a dedicated "ToggleChatMode" keybind (R by default).
- Voice chat now has only one push-to-talk keybind (V by default) which respects the selected chat mode.
- There's now a dedicated "ActiveChat" keybind (T by default) to open the chat using the currently active chat mode.
- If you want to keep the chat keybinds the way they were (separate keybinds for local and radio), you can rebind the "Chat" and "RadioChat" inputs back to T and R and the new "ToggleChatMode" and "ActiveChat" inputs to something else.
Changes:
- Optimized bot AIs: in particular, the cleanup, repair, pump water and load items objectives. Should significantly improve performance when the bots are doing these objectives when there's a large number of items in the sub.
- Optimized entity culling logic (determines which items/structures are currently visible in the screen).
- Optimized a bunch of textures.
- Improved the performance statistics view that's enabled with the "showperf" console command: more fine-grained stats and easier-to-read visuals.
- Added UI volume slider.
- Depth charges can be stored in coilgun ammo shelves.
Fixes:
- Fixed ballast flora branches that have been disconnected from the root not being considered disconnected after a level transition (allowing them to keep growing).
- Fixed "set default bindings" not doing anything in the settings menu.
- Fixed door/hatch gaps not getting moved when snapping to grid in the sub editor.
- Vertically mirrored beds can't be laid on.
- Fixed wrecked reactors being forced to non-interactable even if made interactable in the sub editor.
Unstable only:
- Fixed crashing when you try to edit beacon station settings on a station you haven't saved yet.
- Fixed level difficulties being incorrect in the "normal" difficulty setting (levels were set to the minimum difficulty of the biome, instead of a linear increase across the biome).
- Misc fixes and improvements to the new beacon stations.
- Fixed "max missions per round" value not being restricted in the UI.
- Fixed items not getting transferred to/from linked subs.
Modding:
- Doors and hatches can now be mirrored in the sub editor (making them open from top to bottom, or from right to left).
---------------------------------------------------------------------------------------------------------
v0.18.4.0
---------------------------------------------------------------------------------------------------------
@@ -12,6 +47,7 @@ Unstable only:
- Fixed sprite editor crashing if you try to reload a texture twice.
- Fixed drone/shuttles getting left behind in the outpost when you buy and switch to a sub that has a one.
- Fixed crashing when closing the submarine preview window.
- Disallowed mirroring beds vertically.
Changes:
- Added some new campaign settings: starting balance, amount of starting items and difficulty.
@@ -19,13 +55,17 @@ Changes:
- Made the descriptions of some materials (that used to just say "useful for crafting") more descriptive.
- Increased oxygen generator output in some vanilla subs.
- Made handheld sonar beacon sound less grating.
- Disallowed mirroring beds vertically.
- The client who initiated a vote cannot take part in that vote (except if they're the only client who can vote, in which case the vote automatically passes).
- Made flashlight flicker before the battery runs out.
- Added some lootable money to corpses found in wrecks.
- Removed the small equipment indicators next to the character portrait.
- Added a bunch of new UI sounds (tickbox toggling, confirming transactions, increase/decrease sounds for number inputs, cart sound for adding/removing items in store interfaces, selecting/clicking components, sliders and modlist).
- Optimized many status effects by making them only execute once per second instead of every frame (most importantly, diving suits and volatile fuel rods).
- Optimized talents: buffs are applied to characters periodically instead of every frame.
- Optimized the logic that bots use to determine the safety of hulls.
- Optimized items: stop updating items that don't need to be updated more aggressively.
- Weapon holders now use the tag "mountableweapon" instead of "weapon" to determine which items can be placed in them. Allows tagging non-weapon items as mountable in the holder, without making bots consider it a weapon due to the "weapon" tag. Also allows to keep some weapons not-mountable.
- Ammunition Shelf can now also store Depth Charges ("depthchargeammo" tag added)
Fixes:
- Split campaign state networking messages into multiple ones. Previously all the campaign-related data (map state, reputation, upgrades, purchased items, selected missions) was included in the same message, and whenever anything in the data changed, the server would send all of it to clients. This would cause performance and bandwidth issues in some situations, for example when reputation was changing rapidly.