Build 0.20.9.0

This commit is contained in:
Markus Isberg
2022-12-01 21:59:53 +02:00
parent df805574c4
commit 31d2dc658e
66 changed files with 953 additions and 505 deletions
@@ -9,7 +9,7 @@ using System.Linq;
namespace Barotrauma
{
class CharacterHUD
partial class CharacterHUD
{
const float BossHealthBarDuration = 120.0f;
@@ -99,8 +99,8 @@ namespace Barotrauma
}
}
public static bool ShouldRecreateHudTexts { get; set; } = true;
private static bool heldDownShiftWhenGotHudTexts;
public static bool RecreateHudTexts { get; set; } = true;
private static bool lastHudTextsContextual;
private static float timeHealthWindowClosed;
public static bool IsCampaignInterfaceOpen =>
@@ -218,7 +218,7 @@ namespace Barotrauma
if (focusedItemOverlayTimer <= 0.0f)
{
focusedItem = null;
ShouldRecreateHudTexts = true;
RecreateHudTexts = true;
}
}
}
@@ -340,7 +340,7 @@ namespace Barotrauma
if (focusedItem != character.FocusedItem)
{
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
ShouldRecreateHudTexts = true;
RecreateHudTexts = true;
}
focusedItem = character.FocusedItem;
}
@@ -364,14 +364,14 @@ namespace Barotrauma
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
{
bool shiftDown = PlayerInput.IsShiftDown();
if (ShouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
bool hudTextsContextual = PlayerInput.IsShiftDown();
if (RecreateHudTexts || lastHudTextsContextual != hudTextsContextual)
{
ShouldRecreateHudTexts = true;
heldDownShiftWhenGotHudTexts = shiftDown;
RecreateHudTexts = true;
lastHudTextsContextual = hudTextsContextual;
}
var hudTexts = focusedItem.GetHUDTexts(character, ShouldRecreateHudTexts);
ShouldRecreateHudTexts = false;
var hudTexts = focusedItem.GetHUDTexts(character, RecreateHudTexts);
RecreateHudTexts = false;
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
@@ -790,5 +790,25 @@ namespace Barotrauma
Vector2 drawPos = objectiveEntity.Entity.WorldPosition;// + Vector2.UnitX * objectiveEntity.Sprite.size.X * 1.5f;
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, objectiveEntity.Sprite, objectiveEntity.Color * iconAlpha);
}
static partial void RecreateHudTextsIfControllingProjSpecific(Character character)
{
if (character == Character.Controlled)
{
RecreateHudTexts = true;
}
}
static partial void RecreateHudTextsIfFocusedProjSpecific(params Item[] items)
{
foreach (var item in items)
{
if (item == Character.Controlled?.FocusedItem)
{
RecreateHudTexts = true;
break;
}
}
}
}
}
@@ -30,7 +30,7 @@ namespace Barotrauma
public void ClientExecute(string[] args)
{
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is { IsEditor: true });
if (!allowCheats && !CheatsEnabled && IsCheat)
{
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
@@ -269,6 +269,8 @@ namespace Barotrauma
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
var specializationCount = tree.TalentSubTrees.Count(t => t.Type == TalentTreeType.Specialization);
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
foreach (var subTree in tree.TalentSubTrees)
{
@@ -310,7 +312,7 @@ namespace Barotrauma
for (int i = 0; i < optionAmount; i++)
{
TalentOption option = subTree.TalentOptionStages[i];
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info);
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info, specializationCount);
}
subTreeLayoutGroup.RectTransform.Resize(new Point(subTreeLayoutGroup.Rect.Width,
subTreeLayoutGroup.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
@@ -327,7 +329,12 @@ namespace Barotrauma
var specializationList = GetSpecializationList();
//resize (scale up) children if there's less than 3 of them to make them cover the whole width of the menu
specializationList.Content.RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height),
resizeChildren: specializationList.Content.Children.Count() < 3);
resizeChildren: specializationCount < 3);
//make room for scrollbar if there's more than the default amount of specializations
if (specializationCount > 3)
{
specializationList.RectTransform.MinSize = new Point(specializationList.Rect.Width, specializationList.Content.Rect.Height + (int)(specializationList.ScrollBar.Rect.Height * 0.9f));
}
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
@@ -337,17 +344,17 @@ namespace Barotrauma
{
return specList;
}
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
return newSpecializationList;
}
}
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info)
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info, int specializationCount)
{
int elementPadding = GUI.IntScale(8);
int height = GUI.IntScale((GameMain.GameSession?.Campaign == null ? 65 : 60) * (specializationCount > 3 ? 0.97f : 1.0f));
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.01f), parent.RectTransform, anchor: Anchor.TopCenter)
{ MinSize = new Point(0, GUI.IntScale(65)) }, style: null);
{ MinSize = new Point(0, height) }, style: null);
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
@@ -1,6 +1,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
@@ -117,7 +118,7 @@ namespace Barotrauma
private void UpdateBar()
{
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox))
if (msgBox is { Closed: false } && GUIMessageBox.MessageBoxes.Contains(msgBox))
{
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
{
@@ -129,7 +130,7 @@ namespace Barotrauma
int second = (int)Math.Ceiling(elapsedTime);
if (second > lastSecond)
{
if (msgBox != null && !msgBox.Closed)
if (msgBox is { Closed: false })
{
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
}
@@ -137,6 +138,19 @@ namespace Barotrauma
}
}
private static void CloseLingeringPopups()
{
foreach (GUIComponent box in GUIMessageBox.MessageBoxes.ToImmutableArray())
{
if (box is not GUIMessageBox msgBox) { continue; }
if (msgBox.UserData is PromptData or ResultData)
{
msgBox.Close();
}
}
}
public static void ClientRead(IReadMessage inc)
{
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
@@ -154,6 +168,8 @@ namespace Barotrauma
switch (state)
{
case ReadyCheckState.Start:
CloseLingeringPopups();
bool isOwn = false;
byte authorId = 0;
@@ -175,8 +191,8 @@ namespace Barotrauma
clients.Add(inc.ReadByte());
}
ReadyCheck rCheck = new ReadyCheck(clients,
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
ReadyCheck rCheck = new ReadyCheck(clients,
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime);
crewManager.ActiveReadyCheck = rCheck;
@@ -224,7 +240,7 @@ namespace Barotrauma
if (IsFinished) { return; }
IsFinished = true;
int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes);
int readyCount = Clients.Count(static pair => pair.Value == ReadyStatus.Yes);
int totalCount = Clients.Count;
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
}
@@ -238,31 +254,29 @@ namespace Barotrauma
if (resultsBox == null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) { return; }
if (resultsBox.Content.FindChild(UserListData) is GUIListBox userList)
if (resultsBox.Content.FindChild(UserListData) is not GUIListBox userList) { return; }
// for some reason FindChild doesn't work here?
foreach (GUIComponent child in userList.Content.Children)
{
// for some reason FindChild doesn't work here?
foreach (GUIComponent child in userList.Content.Children)
if (child.UserData is not byte b || b != id) { continue; }
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) { continue; }
string style;
switch (status)
{
if (!(child.UserData is byte b) || b != id) { continue; }
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is GUIImage image)
{
string style;
switch (status)
{
case ReadyStatus.Yes:
style = "MissionCompletedIcon";
break;
case ReadyStatus.No:
style = "MissionFailedIcon";
break;
default:
return;
}
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
}
case ReadyStatus.Yes:
style = "MissionCompletedIcon";
break;
case ReadyStatus.No:
style = "MissionFailedIcon";
break;
default:
return;
}
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
}
}
@@ -327,17 +327,20 @@ namespace Barotrauma.Items.Components
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
int itemPlacement1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f ? 0 : -1;
int itemPlacement2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f ? 0 : -1;
itemPlacement1 += item1.RequiresRecipe && !character.HasRecipeForItem(item1.TargetItem.Identifier) ? -2 : 0;
itemPlacement2 += item2.RequiresRecipe && !character.HasRecipeForItem(item2.TargetItem.Identifier) ? -2 : 0;
int itemPlacement1 = calculatePlacement(item1);
int itemPlacement2 = calculatePlacement(item2);
if (itemPlacement1 != itemPlacement2)
{
return itemPlacement1 > itemPlacement2 ? -1 : 1;
}
int calculatePlacement(FabricationRecipe recipe)
{
int placement = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f ? 0 : -1;
placement += recipe.RequiresRecipe && !AnyOneHasRecipeForItem(character, recipe.TargetItem) ? -2 : 0;
return placement;
}
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
});
@@ -372,7 +375,9 @@ namespace Barotrauma.Items.Components
AutoScaleHorizontal = true,
CanBeFocused = false
};
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && (fabricableItem.RequiresRecipe && !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier)));
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c =>
c.UserData is FabricationRecipe fabricableItem &&
fabricableItem.RequiresRecipe && !AnyOneHasRecipeForItem(character, fabricableItem.TargetItem));
if (firstRequiresRecipe != null)
{
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
@@ -144,6 +144,9 @@ namespace Barotrauma.Items.Components
partial class MiniMap : Powered
{
private Dictionary<Hull, HullData> hullDatas;
private DateTime resetDataTime;
private GUIFrame submarineContainer;
private GUIFrame? hullInfoFrame;
@@ -226,6 +229,8 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific()
{
hullDatas = new Dictionary<Hull, HullData>();
SetDefaultMode();
noPowerTip = TextManager.Get("SteeringNoPowerTip");
@@ -551,6 +556,34 @@ namespace Barotrauma.Items.Components
CreateHUD();
}
//reset data if we haven't received anything in a while
//(so that outdated hull info won't be shown if detectors stop sending signals)
if (DateTime.Now > resetDataTime)
{
foreach (HullData hullData in hullDatas.Values)
{
if (!hullData.Distort)
{
if (Timing.TotalTime > hullData.LastOxygenDataTime + 1.0) { hullData.ReceivedOxygenAmount = null; }
if (Timing.TotalTime > hullData.LastWaterDataTime + 1.0) { hullData.ReceivedWaterAmount = null; }
}
}
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
}
if (cardRefreshTimer > cardRefreshDelay)
{
if (item.Submarine is { } sub)
{
UpdateIDCards(sub);
}
cardRefreshTimer = 0;
}
else
{
cardRefreshTimer += deltaTime;
}
if (scissorComponent != null)
{
if (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
@@ -1736,6 +1769,67 @@ namespace Barotrauma.Items.Components
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
Item source = signal.source;
if (source == null || source.CurrentHull == null) { return; }
Hull sourceHull = source.CurrentHull;
if (!hullDatas.TryGetValue(sourceHull, out HullData? hullData))
{
hullData = new HullData();
hullDatas.Add(sourceHull, hullData);
}
if (hullData.Distort) { return; }
switch (connection.Name)
{
case "water_data_in":
//cheating a bit because water detectors don't actually send the water level
bool fromWaterDetector = source.GetComponent<WaterDetector>() != null;
hullData.ReceivedWaterAmount = null;
hullData.LastWaterDataTime = Timing.TotalTime;
if (fromWaterDetector)
{
hullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(sourceHull);
}
foreach (var linked in sourceHull.linkedTo)
{
if (linked is not Hull linkedHull) { continue; }
if (!hullDatas.TryGetValue(linkedHull, out HullData? linkedHullData))
{
linkedHullData = new HullData();
hullDatas.Add(linkedHull, linkedHullData);
}
linkedHullData.ReceivedWaterAmount = null;
if (fromWaterDetector)
{
linkedHullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(linkedHull);
}
}
break;
case "oxygen_data_in":
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
{
oxy = Rand.Range(0.0f, 100.0f);
}
hullData.ReceivedOxygenAmount = oxy;
hullData.LastOxygenDataTime = Timing.TotalTime;
foreach (var linked in sourceHull.linkedTo)
{
if (linked is not Hull linkedHull) { continue; }
if (!hullDatas.TryGetValue(linkedHull, out HullData? linkedHullData))
{
linkedHullData = new HullData();
hullDatas.Add(linkedHull, linkedHullData);
}
linkedHullData.ReceivedOxygenAmount = oxy;
}
break;
}
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
@@ -396,7 +396,9 @@ namespace Barotrauma.Items.Components
ToolTip = TextManager.Get("reactor.temperatureboostup"),
OnClicked = (_, __) =>
{
applyTemperatureBoost(TemperatureBoostAmount, temperatureBoostSoundUp);
unsentChanges = true;
sendUpdateTimer = 0.0f;
ApplyTemperatureBoost(TemperatureBoostAmount);
return true;
}
};
@@ -407,25 +409,13 @@ namespace Barotrauma.Items.Components
ToolTip = TextManager.Get("reactor.temperatureboostdown"),
OnClicked = (_, __) =>
{
applyTemperatureBoost(-TemperatureBoostAmount, temperatureBoostSoundDown);
unsentChanges = true;
sendUpdateTimer = 0.0f;
ApplyTemperatureBoost(-TemperatureBoostAmount);
return true;
}
};
void applyTemperatureBoost(float amount, RoundSound sound)
{
temperatureBoost = amount;
if (sound != null)
{
SoundPlayer.PlaySound(
sound.Sound,
item.WorldPosition,
sound.Volume,
sound.Range,
hullGuess: item.CurrentHull);
}
}
var graphArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), bottomRightArea.RectTransform))
{
Stretch = true,
@@ -471,6 +461,24 @@ namespace Barotrauma.Items.Components
}
};
}
private void ApplyTemperatureBoost(float amount)
{
if (Math.Abs(temperatureBoost) <= TemperatureBoostAmount * 0.9f &&
Math.Abs(amount) > TemperatureBoostAmount * 0.9f)
{
var sound = amount > 0 ? temperatureBoostSoundUp : temperatureBoostSoundDown;
if (sound != null)
{
SoundPlayer.PlaySound(
sound.Sound,
item.WorldPosition,
sound.Volume,
sound.Range,
hullGuess: item.CurrentHull);
}
}
temperatureBoost = amount;
}
private void InitInventoryUI()
{
@@ -895,6 +903,7 @@ namespace Barotrauma.Items.Components
msg.WriteBoolean(PowerOn);
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(temperatureBoost, -TemperatureBoostAmount, TemperatureBoostAmount, 8);
correctionTimer = CorrectionDelay;
}
@@ -903,7 +912,7 @@ namespace Barotrauma.Items.Components
{
if (correctionTimer > 0.0f)
{
StartDelayedCorrection(msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
StartDelayedCorrection(msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8 + 8), sendingTime);
return;
}
@@ -913,6 +922,7 @@ namespace Barotrauma.Items.Components
TargetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
TargetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8);
ApplyTemperatureBoost(msg.ReadRangedSingle(-TemperatureBoostAmount, TemperatureBoostAmount, 8));
if (Math.Abs(FissionRateScrollBar.BarScroll - TargetFissionRate / 100.0f) > 0.01f)
{
@@ -61,6 +61,7 @@ namespace Barotrauma.Items.Components
private float displayBorderSize;
private List<SonarBlip> sonarBlips;
private readonly HashSet<SonarBlip> prevBlips = new HashSet<SonarBlip>();
private float prevPassivePingRadius;
@@ -812,24 +813,33 @@ namespace Barotrauma.Items.Components
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
float dist = (float)Math.Sqrt(distSqr);
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500 && t.IsWithinSector(transducerCenter))
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
{
int prevBlipCount = sonarBlips.Count;
prevBlips.Clear();
foreach (var blip in sonarBlips)
{
prevBlips.Add(blip);
}
Ping(t.WorldPosition, transducerCenter,
Math.Min(t.SoundRange, range * 0.5f) * displayScale, 0, displayScale, Math.Min(t.SoundRange, range * 0.5f),
t.SoundRange * displayScale, 0, displayScale, range,
passive: true, pingStrength: 0.5f);
sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f));
//remove blips that weren't in the AITarget's sector
if (t.HasSector())
{
for (int i = sonarBlips.Count - 1; i >= prevBlipCount; i--)
for (int i = sonarBlips.Count - 1; i >= 0; i--)
{
if (prevBlips.Contains(sonarBlips[i])) { continue; }
if (!t.IsWithinSector(sonarBlips[i].Position))
{
sonarBlips.RemoveAt(i);
}
}
}
if (t.IsWithinSector(transducerCenter))
{
sonarBlips.Add(new SonarBlip(t.WorldPosition, fadeTimer: 1.0f, scale: MathHelper.Clamp(t.SoundRange / 2000, 1.0f, 5.0f)));
}
}
}
}
@@ -97,7 +97,7 @@ namespace Barotrauma.Items.Components
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), chargeTextContainer.RectTransform, Anchor.CenterRight),
"", textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)capacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, capacity))} %)"
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)adjustedCapacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, adjustedCapacity))} %)"
};
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUIStyle.SmallFont; }
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
{
ProgressGetter = () =>
{
return capacity <= 0.0f ? 1.0f : charge / capacity;
return adjustedCapacity <= 0.0f ? 1.0f : charge / adjustedCapacity;
}
};
}
@@ -126,7 +126,7 @@ namespace Barotrauma.Items.Components
{
if (chargeIndicator != null)
{
float chargeRatio = charge / capacity;
float chargeRatio = charge / adjustedCapacity;
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
}
}
@@ -144,9 +144,9 @@ namespace Barotrauma.Items.Components
Matrix rotate = Matrix.CreateRotationZ(item.RotationRad);
Vector2 center = Vector2.Transform((indicatorPos + (scaledIndicatorSize * 0.5f)) * flip, rotate) + itemPosition;
if (charge > 0 && capacity > 0)
if (charge > 0 && adjustedCapacity > 0)
{
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
float chargeRatio = MathHelper.Clamp(charge / adjustedCapacity, 0.0f, 1.0f);
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
Vector2 indicatorSize;
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
rechargeSpeedSlider.BarScroll = rechargeRate;
}
#endif
Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity;
Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * adjustedCapacity;
}
}
}
@@ -1608,28 +1608,79 @@ namespace Barotrauma
{
containedState = item.Condition / item.MaxCondition;
}
else if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
{
int ignoredItems = itemContainer.AllSubContainableItems == null ? 0 : itemContainer.AllSubContainableItems.Count;
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItems;
containedState = itemCount / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.MainContainerCapacity);
}
else
{
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
var containedItem = itemContainer.Inventory.slots[targetSlot].FirstOrDefault();
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
ItemSlot containedItemSlot = null;
if (targetSlot < itemContainer.Inventory.slots.Length)
{
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
containedItemSlot = itemContainer.Inventory.slots[targetSlot];
}
if (containedItemSlot != null)
{
Item containedItem = containedItemSlot.FirstOrDefault();
if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
{
containedState = itemContainer.Inventory.slots[targetSlot].Items.Count / (float)maxStackSize;
if (containedItem == null)
{
// No item on the defined slot, check if the items on other slots can be used.
containedItem = containedItemSlot.FirstOrDefault() ?? itemContainer.Inventory.AllItems.FirstOrDefault(it => itemContainer.CanBeContained(it, targetSlot));
}
if (containedItem != null)
{
int ignoredItemCount = 0;
var subContainableItems = itemContainer.AllSubContainableItems;
float capacity = itemContainer.GetMaxStackSize(targetSlot);
if (subContainableItems != null)
{
bool useMainContainerCapacity = true;
foreach (Item it in itemContainer.Inventory.AllItems)
{
// Ignore all items in the sub containers.
foreach (RelatedItem ri in subContainableItems)
{
if (ri.MatchesItem(containedItem))
{
// The target item is in a subcontainer -> inverse the logic.
useMainContainerCapacity = false;
break;
}
if (ri.MatchesItem(it))
{
ignoredItemCount++;
}
}
if (!useMainContainerCapacity) { break; }
}
if (useMainContainerCapacity)
{
capacity *= itemContainer.MainContainerCapacity;
}
else
{
// Ignore all items in the main container.
ignoredItemCount = itemContainer.Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
capacity *= itemContainer.Capacity - itemContainer.MainContainerCapacity;
}
}
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItemCount;
containedState = Math.Min(itemCount / Math.Max(capacity, 1), 1);
}
}
else
{
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
{
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
{
containedState = containedItemSlot.Items.Count / (float)maxStackSize;
}
}
}
}
}
@@ -1251,11 +1251,9 @@ namespace Barotrauma
{
foreach (ItemComponent ic in components)
{
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
Color color = Color.Gray;
if (ic.HasRequiredItems(character, false))
{
@@ -1268,6 +1266,7 @@ namespace Barotrauma
color = Color.Cyan;
}
}
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
}
}
@@ -1,12 +1,15 @@
using System.Diagnostics;
using System.IO.Pipes;
using System.Linq;
using System.Threading;
namespace Barotrauma.Networking
{
static partial class ChildServerRelay
{
public static Process Process;
public static bool IsProcessAlive => Process is { HasExited: false };
private static bool localHandlesDisposed;
private static AnonymousPipeServerStream writePipe;
private static AnonymousPipeServerStream readPipe;
@@ -44,18 +47,27 @@ namespace Barotrauma.Networking
localHandlesDisposed = true;
}
public static void ClosePipes()
public static void AttemptGracefulShutDown(int maxAttempts = 20)
{
writePipe?.Dispose(); writePipe = null;
readPipe?.Dispose(); readPipe = null;
shutDown = true;
status = StatusEnum.RequestedShutDown;
writeManualResetEvent?.Set();
int checks = 0;
while (Process is { HasExited: false })
{
if (checks >= maxAttempts)
{
DebugConsole.AddWarning("Server could not be shut down gracefully");
break;
}
Thread.Sleep(100);
checks++;
}
ForceShutDown();
}
public static void ShutDown()
public static void ForceShutDown()
{
Process?.Kill(); Process = null;
writePipe = null; readPipe = null;
PrivateShutDown();
}
@@ -332,10 +332,27 @@ namespace Barotrauma.Networking
};
}
public void CreateServerCrashMessage()
{
// Close any message boxes that say "The server has crashed."
var basicServerCrashMsg = TextManager.Get($"{nameof(DisconnectReason)}.{nameof(DisconnectReason.ServerCrashed)}");
GUIMessageBox.MessageBoxes
.OfType<GUIMessageBox>()
.Where(mb => mb.Text?.Text == basicServerCrashMsg)
.ToArray()
.ForEach(mb => mb.Close());
// Open a new message box with the crash report path
if (GUIMessageBox.MessageBoxes.All(
mb => (mb as GUIMessageBox)?.Text?.Text != ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
}
}
private bool ReturnToPreviousMenu(GUIButton button, object obj)
{
Quit();
Submarine.Unload();
GameMain.Client = null;
GameMain.GameSession = null;
@@ -531,14 +548,10 @@ namespace Barotrauma.Networking
{
if (GameMain.WindowActive)
{
if (ChildServerRelay.Process?.HasExited ?? true)
if (!ChildServerRelay.IsProcessAlive)
{
Quit();
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
}
CreateServerCrashMessage();
}
}
}
@@ -942,13 +955,6 @@ namespace Barotrauma.Networking
GUI.ClearCursorWait();
ChildServerRelay.ShutDown();
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
}
if (disconnectPacket.ShouldCreateAnalyticsEvent)
{
GameAnalyticsManager.AddErrorEventOnce(
@@ -974,11 +980,43 @@ namespace Barotrauma.Networking
}
else
{
ReturnToPreviousMenu(null, null);
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
if (ClientPeer is SteamP2PClientPeer or SteamP2POwnerPeer)
{
DisplayInLoadingScreens = true
};
SteamManager.LeaveLobby();
}
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
CampaignMode.StartRoundCancellationToken?.Cancel();
if (SteamManager.IsInitialized)
{
Steamworks.SteamFriends.ClearRichPresence();
}
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
{
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
}
ChildServerRelay.AttemptGracefulShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove();
VoipClient?.Dispose();
VoipClient = null;
GameMain.Client = null;
GameMain.GameSession = null;
ReturnToPreviousMenu(null, null);
if (disconnectPacket.DisconnectReason != DisconnectReason.Disconnected)
{
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
{
DisplayInLoadingScreens = true
};
}
}
}
@@ -2538,46 +2576,9 @@ namespace Barotrauma.Networking
public void Quit()
{
if (ClientPeer is SteamP2PClientPeer || ClientPeer is SteamP2POwnerPeer)
{
SteamManager.LeaveLobby();
}
GameMain.ModDownloadScreen.Reset();
ContentPackageManager.EnabledPackages.Restore();
CampaignMode.StartRoundCancellationToken?.Cancel();
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
ClientPeer = null;
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
{
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
}
if (ChildServerRelay.Process != null)
{
int checks = 0;
while (ChildServerRelay.Process is { HasExited: false })
{
if (checks > 10)
{
ChildServerRelay.ShutDown();
}
Thread.Sleep(100);
checks++;
}
}
ChildServerRelay.ShutDown();
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
characterInfo?.Remove();
VoipClient?.Dispose();
VoipClient = null;
GameMain.Client = null;
GameMain.GameSession = null;
}
public void SendCharacterInfo(string newName = null)
@@ -91,15 +91,11 @@ namespace Barotrauma.Networking
ToolBox.ThrowIfNull(netClient);
ToolBox.ThrowIfNull(incomingLidgrenMessages);
if (isOwner && !(ChildServerRelay.Process is { HasExited: false }))
if (isOwner && !ChildServerRelay.IsProcessAlive)
{
var gameClient = GameMain.Client;
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += (btn, obj) =>
{
GameMain.MainMenuScreen.Select();
return false;
};
gameClient?.CreateServerCrashMessage();
return;
}
@@ -187,15 +187,11 @@ namespace Barotrauma.Networking
{
if (!isActive) { return; }
if (ChildServerRelay.HasShutDown || !(ChildServerRelay.Process is { HasExited: false }))
if (ChildServerRelay.HasShutDown || !ChildServerRelay.IsProcessAlive)
{
var gameClient = GameMain.Client;
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += (btn, obj) =>
{
GameMain.MainMenuScreen.Select();
return false;
};
gameClient?.CreateServerCrashMessage();
return;
}
@@ -401,8 +397,6 @@ namespace Barotrauma.Networking
ClosePeerSession(remotePeers[i]);
}
ChildServerRelay.ClosePipes();
callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
SteamManager.LeaveLobby();
@@ -17,9 +17,7 @@ namespace Barotrauma.Networking
get;
private set;
}
public static IReadOnlyList<string> CaptureDeviceNames =>
Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
private readonly IntPtr captureDevice;
@@ -169,6 +167,11 @@ namespace Barotrauma.Networking
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, storedBufferID);
}
public static IReadOnlyList<string> GetCaptureDeviceNames()
{
return Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
}
IntPtr nativeBuffer;
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
@@ -10,8 +10,6 @@ namespace Barotrauma
{
partial class GameScreen : Screen
{
public override bool IsEditor => GameMain.GameSession?.GameMode is TestGameMode;
private RenderTarget2D renderTargetBackground;
private RenderTarget2D renderTarget;
private RenderTarget2D renderTargetWater;
@@ -47,7 +47,14 @@ namespace Barotrauma
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
private GUIDropDown serverExecutableDropdown;
private readonly GUIButton joinServerButton, hostServerButton, steamWorkshopButton;
private readonly GUIButton joinServerButton, hostServerButton;
private readonly GUIFrame modsButtonContainer;
private readonly GUIButton modsButton, modUpdatesButton;
private Task<IReadOnlyList<Steamworks.Ugc.Item>> modUpdateTask;
private float modUpdateTimer = 0.0f;
private const float ModUpdateInterval = 60.0f;
private readonly GameMain game;
private GUIImage playstyleBanner;
@@ -268,15 +275,29 @@ namespace Barotrauma
RelativeSpacing = 0.035f
};
#if USE_STEAM
steamWorkshopButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
modsButtonContainer = new GUIFrame(new RectTransform(Vector2.One, customizeList.RectTransform),
style: null);
modsButton = new GUIButton(new RectTransform(Vector2.One, modsButtonContainer.RectTransform),
TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{
ForceUpperCase = ForceUpperCase.Yes,
Enabled = true,
UserData = Tab.SteamWorkshop,
OnClicked = SelectTab
};
#endif
modUpdatesButton = new GUIButton(new RectTransform(Vector2.One * 0.95f, modsButtonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight),
style: "GUIUpdateButton")
{
ToolTip = TextManager.Get("ModUpdatesAvailable"),
OnClicked = (_, _) =>
{
BulkDownloader.PrepareUpdates();
return false;
},
Visible = false
};
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
{
@@ -525,6 +546,8 @@ namespace Barotrauma
#region Selection
public override void Select()
{
ResetModUpdateButton();
if (WorkshopItemsToUpdate.Any())
{
while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
@@ -711,6 +734,13 @@ namespace Barotrauma
}
#endregion
public void ResetModUpdateButton()
{
modUpdateTask = null;
modUpdateTimer = 0;
modUpdatesButton.Visible = false;
}
public void QuickStart(bool fixedSeed = false, Identifier sub = default, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
{
if (fixedSeed)
@@ -930,15 +960,32 @@ namespace Barotrauma
public override void Update(double deltaTime)
{
#if !DEBUG && USE_STEAM
modUpdateTimer -= (float)deltaTime;
if (modUpdateTimer <= 0.0f && modUpdateTask is not { IsCompleted: false })
{
modUpdateTask = BulkDownloader.GetItemsThatNeedUpdating();
modUpdateTimer = ModUpdateInterval;
}
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
{
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
}
steamWorkshopButton.Enabled = Steam.SteamManager.IsInitialized;
#elif USE_STEAM
steamWorkshopButton.Enabled = true;
#endif
if (modUpdateTask is { IsCompletedSuccessfully: true })
{
modUpdatesButton.Visible = modUpdateTask.Result.Count > 0;
}
if (modUpdatesButton.Visible)
{
var modButtonLabelSize =
modsButton.Font.MeasureString(modsButton.Text).ToPoint()
+ new Point(GUI.IntScale(25));
modUpdatesButton.RectTransform.AbsoluteOffset =
(modButtonLabelSize.X, modsButton.Rect.Height / 2 - modUpdatesButton.Rect.Height / 2);
}
switch (selectedTab)
{
case Tab.NewGame:
@@ -103,6 +103,10 @@ namespace Barotrauma
public GUIFrame JobPreferenceContainer;
public GUIListBox JobList;
private Identifier micIconStyle;
private float micCheckTimer;
const float MicCheckInterval = 1.0f;
private float autoRestartTimer;
//persistent characterinfo provided by the server
@@ -2656,27 +2660,9 @@ namespace Barotrauma
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (GameMain.Client == null) { return; }
Identifier currMicStyle = micIcon.Style.Element.NameAsIdentifier();
Identifier targetMicStyle = "GUIMicrophoneEnabled".ToIdentifier();
var voipCaptureDeviceNames = VoipCapture.CaptureDeviceNames;
if (voipCaptureDeviceNames.Count == 0)
{
targetMicStyle = "GUIMicrophoneUnavailable".ToIdentifier();
}
else if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
{
targetMicStyle = "GUIMicrophoneDisabled".ToIdentifier();
}
if (targetMicStyle != currMicStyle)
{
GUIStyle.Apply(micIcon, targetMicStyle);
}
UpdateMicIcon((float)deltaTime);
foreach (GUIComponent child in PlayerList.Content.Children)
{
@@ -2738,6 +2724,35 @@ namespace Barotrauma
if (!mouseRect.Contains(PlayerInput.MousePosition)) { jobVariantTooltip = null; }
}
}
private void UpdateMicIcon(float deltaTime)
{
micCheckTimer -= deltaTime;
if (micCheckTimer > 0.0f) { return; }
Identifier newMicIconStyle = "GUIMicrophoneEnabled".ToIdentifier();
if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
{
newMicIconStyle = "GUIMicrophoneDisabled".ToIdentifier();
}
else
{
var voipCaptureDeviceNames = VoipCapture.GetCaptureDeviceNames();
if (voipCaptureDeviceNames.Count == 0)
{
newMicIconStyle = "GUIMicrophoneUnavailable".ToIdentifier();
}
}
if (newMicIconStyle != micIconStyle)
{
micIconStyle = newMicIconStyle;
GUIStyle.Apply(micIcon, newMicIconStyle);
}
micCheckTimer = MicCheckInterval;
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(Color.Black);
@@ -354,9 +354,24 @@ namespace Barotrauma
ToolTip = RichString.Rich(TextManager.Get("SaveSubButton") + "‖color:125,125,125‖\nCtrl + S‖color:end‖"),
OnClicked = (btn, data) =>
{
#if DEBUG
if (ContentPackageManager.EnabledPackages.All.Any(cp => cp != ContentPackageManager.VanillaCorePackage && cp.Files.Any(f => f is not BaseSubFile)))
{
var msgBox = new GUIMessageBox("DEBUG-ONLY WARNING", "You currently have some mods enabled. Are you sure you want to save the submarine? If the mods override any vanilla content, saving the submarine may cause unintended changes.",
new LocalizedString[] { "Yes, I know what I'm doing", "Cancel" });
msgBox.Buttons[0].OnClicked = (btn, data) =>
{
msgBox.Close();
loadFrame = null;
CreateSaveScreen();
return true;
};
msgBox.Buttons[1].OnClicked += msgBox.Close;
return false;
}
#endif
loadFrame = null;
CreateSaveScreen();
return true;
}
};
@@ -3096,7 +3111,7 @@ namespace Barotrauma
return false;
}
private void SnapToGrid()
private static void SnapToGrid()
{
// First move components
foreach (MapEntity e in MapEntity.SelectedList)
@@ -3109,6 +3124,10 @@ namespace Barotrauma
var wire = item.GetComponent<Wire>();
if (wire != null) { continue; }
item.Move(offset);
if (item.GetComponent<Door>()?.LinkedGap is Gap linkedGap)
{
linkedGap.Move(item.Position - linkedGap.Position);
}
}
else if (e is Structure structure)
{
@@ -3133,7 +3152,7 @@ namespace Barotrauma
}
}
private IEnumerable<SubmarineInfo> GetLoadableSubs()
private static IEnumerable<SubmarineInfo> GetLoadableSubs()
{
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
return SubmarineInfo.SavedSubmarines.Where(s
@@ -3520,9 +3539,18 @@ namespace Barotrauma
public void LoadSub(SubmarineInfo info)
{
Submarine.Unload();
var selectedSub = new Submarine(info);
MainSub = selectedSub;
MainSub.UpdateTransform(interpolate: false);
Submarine selectedSub = null;
try
{
selectedSub = new Submarine(info);
MainSub = selectedSub;
MainSub.UpdateTransform(interpolate: false);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to load the submarine. The submarine file might be corrupted.", e);
return;
}
ClearUndoBuffer();
CreateDummyCharacter();
@@ -716,8 +716,7 @@ namespace Barotrauma
if (Screen.Selected == null) { return "menu".ToIdentifier(); }
if ((Screen.Selected?.IsEditor ?? false)
|| (Screen.Selected == GameMain.NetLobbyScreen))
if (Screen.Selected is { IsEditor: true } || GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected == GameMain.NetLobbyScreen)
{
return "editor".ToIdentifier();
}
@@ -814,7 +813,7 @@ namespace Barotrauma
{
return "levelend".ToIdentifier();
}
if (GameMain.GameSession.RoundDuration > 120.0 &&
if (GameMain.GameSession.RoundDuration < 120.0 &&
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
{
return "start".ToIdentifier();
@@ -64,7 +64,7 @@ namespace Barotrauma.Steam
});
}
private static async Task<IReadOnlyList<Steamworks.Ugc.Item>> GetItemsThatNeedUpdating()
public static async Task<IReadOnlyList<Steamworks.Ugc.Item>> GetItemsThatNeedUpdating()
{
var determiningTasks = ContentPackageManager.WorkshopPackages.Select(async p => (p, await p.IsUpToDate()));
(ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks);
@@ -126,6 +126,7 @@ namespace Barotrauma.Steam
{
mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
}
GameMain.MainMenuScreen.ResetModUpdateButton();
});
}