diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs index a911b5c70..b6304d486 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs @@ -484,7 +484,9 @@ namespace Barotrauma (int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f), (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)), character.Info.IsDisguisedAsAnother); - character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother); + float yOffset = (GameMain.GameSession?.Campaign is MultiPlayerCampaign ? -10 : 4) * GUI.Scale; + character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, yOffset), targetWidth: HUDLayoutSettings.PortraitArea.Width, true, character.Info.IsDisguisedAsAnother); + character.Info.DrawForeground(spriteBatch); } mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); if (mouseOnPortrait) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs index 8ee874c7d..95e85187d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs @@ -309,6 +309,32 @@ namespace Barotrauma HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height)); } + public void DrawForeground(SpriteBatch spriteBatch) + { + if (Character is null || GameMain.IsSingleplayer) { return; } + const int million = 1000000; + int xfraction = (int)(HUDLayoutSettings.BottomRightInfoArea.Width * 0.2f); + int yoffset = GUI.IntScale(6); + + int walletAmount = Character.Wallet.Balance; + + LocalizedString str = walletAmount >= million ? TextManager.Get("crewwallet.balance.toomuchtoshow") : TextManager.FormatCurrency(walletAmount); + Vector2 size = GUIStyle.Font.MeasureString(str); + int barHeight = GUI.IntScale(18); + + Rectangle barRect = new Rectangle((int)(HUDLayoutSettings.BottomRightInfoArea.X + xfraction / 2.5f), HUDLayoutSettings.BottomRightInfoArea.Bottom - barHeight - yoffset, HUDLayoutSettings.BottomRightInfoArea.Width - xfraction, barHeight); + float textScale = Math.Max(0.1f, Math.Min(barRect.Width / size.X, barRect.Height / size.Y)) - 0.01f; + + GUIStyle.WalletPortraitBG.Draw(spriteBatch, barRect, Color.White); + + int iconSize = GUI.IntScale(28); + int iconXOffset = iconSize / 2; + Rectangle iconRect = new Rectangle(barRect.Right - iconXOffset, barRect.Top - iconSize / 4, iconSize, iconSize); + GUIStyle.CrewWalletIconSmall.Draw(spriteBatch, iconRect, Color.White); + var (scaledTextSizeX, scaledTextSizeY) = size * textScale; + GUIStyle.Font.DrawString(spriteBatch, str, new Vector2(barRect.Right - iconXOffset - scaledTextSizeX - GUI.IntScale(4), barRect.Center.Y - scaledTextSizeY / 2), GUIStyle.TextColorNormal, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); + } + public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false, bool evaluateDisguise = false) { if (evaluateDisguise && IsDisguised) { return; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs index efaaebbc6..0089c3e94 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs @@ -512,23 +512,36 @@ namespace Barotrauma } /// - /// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled. + /// Scrolls the list to the specific element. /// /// - public void ScrollToElement(GUIComponent component) + public void ScrollToElement(GUIComponent component, bool playSound = true) { - SoundPlayer.PlayUISound(GUISoundType.Click); + if (playSound) { SoundPlayer.PlayUISound(GUISoundType.Click); } List children = Content.Children.ToList(); int index = children.IndexOf(component); if (index < 0) { return; } + void performScroll(GUIComponent c) + { + if (SmoothScroll && PadBottom) + { + scrollToElement = c; + } + else + { + float diff = isHorizontal ? c.Rect.X - Content.Rect.X : c.Rect.Y - Content.Rect.Y; + ScrollBar.BarScroll += diff / TotalSize; + } + } + if (!Content.Children.Contains(component) || !component.Visible) { - scrollToElement = null; + performScroll(null); } else { - scrollToElement = component; + performScroll(component); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs index 0c8828ae5..850f347bb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs @@ -64,6 +64,8 @@ namespace Barotrauma public readonly static GUISprite UIGlowSolidCircular = new GUISprite("UIGlowSolidCircular"); public readonly static GUISprite UIThermalGlow = new GUISprite("UIGlowSolidCircular"); public readonly static GUISprite ButtonPulse = new GUISprite("ButtonPulse"); + public readonly static GUISprite WalletPortraitBG = new GUISprite("WalletPortraitBG"); + public readonly static GUISprite CrewWalletIconSmall = new GUISprite("CrewWalletIconSmall"); public readonly static GUISprite EndRoundButtonPulse = new GUISprite("EndRoundButtonPulse"); @@ -96,6 +98,11 @@ namespace Barotrauma /// public readonly static GUIColor Yellow = new GUIColor("Yellow"); + /// + /// Color to display the name of modded servers in the server list. + /// + public readonly static GUIColor ModdedServerColor = new GUIColor("ModdedServerColor"); + public readonly static GUIColor ColorInventoryEmpty = new GUIColor("ColorInventoryEmpty"); public readonly static GUIColor ColorInventoryHalf = new GUIColor("ColorInventoryHalf"); public readonly static GUIColor ColorInventoryFull = new GUIColor("ColorInventoryFull"); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs index 8a495614b..bdad817ee 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs @@ -50,6 +50,7 @@ namespace Barotrauma private GUIImage sellValueChangeArrow; private GUIDropDown sortingDropDown; private GUITextBox searchBox; + private GUILayoutGroup categoryButtonContainer; private GUIListBox storeBuyList, storeSellList, storeSellFromSubList; /// /// Can be null when there are no deals at the current location @@ -482,7 +483,7 @@ namespace Barotrauma }; // Item category buttons ------------------------------------------------ - var categoryButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.08f, 1.0f), storeInventoryContainer.RectTransform)) + categoryButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.08f, 1.0f), storeInventoryContainer.RectTransform)) { RelativeSpacing = 0.02f }; @@ -765,6 +766,34 @@ namespace Barotrauma } } + private void UpdateCategoryButtons() + { + var tabItems = activeTab switch + { + StoreTab.Buy => ActiveStore?.Stock, + StoreTab.Sell => itemsToSell, + StoreTab.SellSub => itemsToSellFromSub, + _ => null + } ?? Enumerable.Empty(); + foreach (var button in itemCategoryButtons) + { + if (!(button.UserData is MapEntityCategory category)) + { + continue; + } + bool isButtonEnabled = false; + foreach (var item in tabItems) + { + if (item.ItemPrefab.Category.HasFlag(category)) + { + isButtonEnabled = true; + break; + } + } + button.Enabled = isButtonEnabled; + } + } + private void ChangeStoreTab(StoreTab tab) { activeTab = tab; @@ -774,6 +803,7 @@ namespace Barotrauma } sortingDropDown.SelectItem(tabSortingMethods[tab]); relevantBalanceName.Text = IsBuying ? TextManager.Get("campaignstore.balance") : TextManager.Get("campaignstore.storebalance"); + UpdateCategoryButtons(); SetShoppingCrateTotalText(); SetClearAllButtonStatus(); SetConfirmButtonBehavior(); @@ -879,7 +909,7 @@ namespace Barotrauma prevDailySpecialCount = dailySpecialCount; } - bool hasPermissions = HasTabPermissions(StoreTab.Sell); + bool hasPermissions = HasTabPermissions(StoreTab.Buy); var existingItemFrames = new HashSet(); foreach (PurchasedItem item in ActiveStore.Stock) { @@ -931,7 +961,11 @@ namespace Barotrauma removedItemFrames.AddRange(storeDailySpecialsGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList()); } removedItemFrames.ForEach(f => f.RectTransform.Parent = null); - if (activeTab == StoreTab.Buy) { FilterStoreItems(); } + if (activeTab == StoreTab.Buy) + { + UpdateCategoryButtons(); + FilterStoreItems(); + } SortItems(StoreTab.Buy); storeBuyList.BarScroll = prevBuyListScroll; @@ -1011,7 +1045,11 @@ namespace Barotrauma } removedItemFrames.ForEach(f => f.RectTransform.Parent = null); - if (activeTab == StoreTab.Sell) { FilterStoreItems(); } + if (activeTab == StoreTab.Sell) + { + UpdateCategoryButtons(); + FilterStoreItems(); + } SortItems(StoreTab.Sell); storeSellList.BarScroll = prevSellListScroll; @@ -1091,7 +1129,11 @@ namespace Barotrauma } removedItemFrames.ForEach(f => f.RectTransform.Parent = null); - if (activeTab == StoreTab.SellSub) { FilterStoreItems(); } + if (activeTab == StoreTab.SellSub) + { + UpdateCategoryButtons(); + FilterStoreItems(); + } SortItems(StoreTab.SellSub); storeSellFromSubList.BarScroll = prevSellListScroll; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs index da6b8d6d1..d8125d1fb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs @@ -50,19 +50,23 @@ namespace Barotrauma private const ushort lowPingThreshold = 100; private const ushort mediumPingThreshold = 200; + public readonly Client Client; + private ushort currentPing; - private readonly Client client; private readonly Character character; private readonly bool hasCharacter; private readonly GUITextBlock textBlock; private readonly GUIFrame frame; - public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock) + private readonly GUIImage permissionIcon; + + public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock, GUIImage permissionIcon) { - this.client = client; + this.Client = client; this.textBlock = textBlock; this.frame = frame; this.hasCharacter = hasCharacter; + this.permissionIcon = permissionIcon; } public LinkedGUI(Character character, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock) @@ -75,33 +79,39 @@ namespace Barotrauma public bool HasMultiplayerCharacterChanged() { - if (client == null) return false; - bool characterState = client.Character != null; - if (characterState && client.Character.IsDead) characterState = false; + if (Client == null) { return false; } + bool characterState = Client.Character != null; + if (characterState && Client.Character.IsDead) characterState = false; return hasCharacter != characterState; } public bool HasMultiplayerCharacterDied() { - if (client == null || !hasCharacter || client.Character == null) return false; - return client.Character.IsDead; + if (Client == null || !hasCharacter || Client.Character == null) { return false; } + return Client.Character.IsDead; } public bool HasAICharacterDied() { - if (character == null) return false; + if (character == null) { return false; } return character.IsDead; } public void TryPingRefresh() { - if (client == null) return; - if (currentPing == client.Ping) return; - currentPing = client.Ping; + if (Client == null) { return; } + if (currentPing == Client.Ping) { return; } + currentPing = Client.Ping; textBlock.Text = currentPing.ToString(); textBlock.TextColor = GetPingColor(); } + public void TryPermissionIconRefresh(Sprite icon) + { + if (Client == null || permissionIcon == null) { return; } + permissionIcon.Sprite = icon; + } + private Color GetPingColor() { if (currentPing < lowPingThreshold) @@ -196,6 +206,7 @@ namespace Barotrauma for (int i = 0; i < linkedGUIList.Count; i++) { linkedGUIList[i].TryPingRefresh(); + linkedGUIList[i].TryPermissionIconRefresh(GetPermissionIcon(linkedGUIList[i].Client)); if (linkedGUIList[i].HasMultiplayerCharacterChanged() || linkedGUIList[i].HasMultiplayerCharacterDied() || linkedGUIList[i].HasAICharacterDied()) { RemoveCurrentElements(); @@ -549,31 +560,42 @@ namespace Barotrauma GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor); - linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, null)); + linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, textBlock: null)); } private void CreateMultiPlayerListContentHolder(GUILayoutGroup headerFrame) { + bool isCampaign = GameMain.GameSession?.Campaign is MultiPlayerCampaign; GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale"); GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale"); GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale"); - GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale"); + if (isCampaign) + { + GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform) + { + RelativeSize = new Vector2(walletColumnWidthPercentage * sizeMultiplier, 1f) + }, TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale") + { + TextBlock = { Font = GUIStyle.HotkeyFont }, + CanBeFocused = false, + ForceUpperCase = ForceUpperCase.Yes + }; + walletColumnWidth = walletButton.Rect.Width; + } sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width; jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f); - characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f); + characterButton.RectTransform.RelativeSize = new Vector2((characterColumnWidthPercentage + (isCampaign ? 0 : walletColumnWidthPercentage)) * sizeMultiplier, 1f); pingButton.RectTransform.RelativeSize = new Vector2(pingColumnWidthPercentage * sizeMultiplier, 1f); - walletButton.RectTransform.RelativeSize = new Vector2(walletColumnWidthPercentage * sizeMultiplier, 1f); - jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = walletButton.TextBlock.Font = GUIStyle.HotkeyFont; - jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = walletButton.CanBeFocused = false; - jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = walletButton.ForceUpperCase = ForceUpperCase.Yes; + jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = GUIStyle.HotkeyFont; + jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = false; + jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = ForceUpperCase.Yes; jobColumnWidth = jobButton.Rect.Width; characterColumnWidth = characterButton.Rect.Width; pingColumnWidth = pingButton.Rect.Width; - walletColumnWidth = walletButton.Rect.Width; } private void CreateMultiPlayerList(bool refresh) @@ -634,8 +656,10 @@ namespace Barotrauma if (client != null) { - CreateNameWithPermissionIcon(client, paddedFrame); - linkedGUIList.Add(new LinkedGUI(client, frame, true, new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center))); + CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon); + linkedGUIList.Add(new LinkedGUI(client, frame, true, + new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center), + permissionIcon)); } else { @@ -644,11 +668,12 @@ namespace Barotrauma if (character is AICharacter) { - linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes })); + linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, + new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes })); } else { - linkedGUIList.Add(new LinkedGUI(client: null, frame, true, null)); + linkedGUIList.Add(new LinkedGUI(client: null, frame, true, textBlock: null, permissionIcon: null)); new GUICustomComponent(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => DrawDisconnectedIcon(sb, component.Rect)) { @@ -686,12 +711,15 @@ namespace Barotrauma SelectedColor = Color.White }; - CreateNameWithPermissionIcon(client, paddedFrame); + CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon); + linkedGUIList.Add(new LinkedGUI(client, frame, false, + new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center), + permissionIcon)); + if (client.Character is { } character) { CreateWalletCrewFrame(character, paddedFrame); } - linkedGUIList.Add(new LinkedGUI(client, frame, false, new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center))); } private int GetTeamIndex(Client client) @@ -729,21 +757,47 @@ namespace Barotrauma private void CreateWalletCrewFrame(Character character, GUILayoutGroup paddedFrame) { + if (!(GameMain.GameSession?.Campaign is MultiPlayerCampaign)) { return; } + GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center) { CanBeFocused = false }; - if (character.IsBot) { return; } - GUILayoutGroup paddedLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1f), walletLayout.RectTransform, Anchor.Center), isHorizontal: true) { Stretch = true }; - GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "StoreTradingIcon", scaleToFit: true); - GUITextBlock walletBlock = new GUITextBlock(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), string.Empty, textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont); - SetWalletText(walletBlock, character.Wallet); + if (character.IsBot) { return; } + + Sprite walletSprite = GUIStyle.CrewWalletIconSmall.Value.Sprite; + + GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), walletSprite, scaleToFit: true); + GUITextBlock walletBlock = new GUITextBlock(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), string.Empty, textAlignment: Alignment.Right, font: GUIStyle.Font) + { + AutoScaleHorizontal = true, + Padding = Vector4.Zero + }; + + GUIImage largeIcon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), walletSprite, scaleToFit: true) + { + IgnoreLayoutGroups = true, + Visible = false + }; + + if (character.IsBot) + { + largeIcon.Visible = true; + icon.Visible = false; + walletBlock.Visible = false; + largeIcon.Enabled = false; + return; + } + + walletLayout.Recalculate(); + paddedLayoutGroup.Recalculate(); + SetWalletText(walletBlock, character.Wallet, icon, largeIcon); if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign) { @@ -751,47 +805,56 @@ namespace Barotrauma campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e => { if (!(e.Owner is Some { Value: var owner }) || owner != character) { return; } - SetWalletText(walletBlock, e.Wallet); + SetWalletText(walletBlock, e.Wallet, icon, largeIcon); }); registeredEvents.Add(eventIdentifier); } - static void SetWalletText(GUITextBlock block, Wallet wallet) + static void SetWalletText(GUITextBlock block, Wallet wallet, GUIImage icon, GUIImage largeIcon) { + const int million = 1000000, + tooSmallPixelTreshold = 50; // 50 pixels is just not enough to see any meaningful info + block.Text = TextManager.FormatCurrency(wallet.Balance); block.ToolTip = string.Empty; - if (block.TextSize.X + block.Padding.X + block.Padding.Z > block.Rect.Width) + + if (wallet.Balance >= million) { - block.ToolTip = block.Text; block.Text = TextManager.Get("crewwallet.balance.toomuchtoshow"); + block.ToolTip = block.Text; + } + + largeIcon.Visible = false; + icon.Visible = true; + block.Visible = true; + + if (tooSmallPixelTreshold > block.Rect.Width) + { + largeIcon.Visible = true; + icon.Visible = false; + block.Visible = false; + largeIcon.ToolTip = block.Text; } } } - private void CreateNameWithPermissionIcon(Client client, GUILayoutGroup paddedFrame) + private void CreateNameWithPermissionIcon(Client client, GUILayoutGroup paddedFrame, out GUIImage permissionIcon) { GUITextBlock characterNameBlock; - Sprite permissionIcon = GetPermissionIcon(client); + Sprite permissionIconSprite = GetPermissionIcon(client); JobPrefab prefab = client.Character?.Info?.Job?.Prefab; Color nameColor = prefab != null ? prefab.UIColor : Color.White; - if (permissionIcon != null) - { - Point iconSize = permissionIcon.SourceRect.Size; - float characterNameWidthAdjustment = (iconSize.X + paddedFrame.AbsoluteSpacing) / characterColumnWidth; + Point iconSize = new Point((int)(paddedFrame.Rect.Height * 0.8f)); + float characterNameWidthAdjustment = (iconSize.X + paddedFrame.AbsoluteSpacing) / characterColumnWidth; - characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), - ToolBox.LimitString(client.Name, GUIStyle.Font, (int)(characterColumnWidth - paddedFrame.Rect.Width * characterNameWidthAdjustment)), textAlignment: Alignment.Center, textColor: nameColor); + characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), + ToolBox.LimitString(client.Name, GUIStyle.Font, (int)(characterColumnWidth - paddedFrame.Rect.Width * characterNameWidthAdjustment)), textAlignment: Alignment.Center, textColor: nameColor); - float iconWidth = iconSize.X / (float)characterColumnWidth; - int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUIStyle.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width); - new GUIImage(new RectTransform(new Vector2(iconWidth, 1f), paddedFrame.RectTransform) { AbsoluteOffset = new Point(xOffset + 2, 0) }, permissionIcon) { IgnoreLayoutGroups = true }; - } - else - { - characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), - ToolBox.LimitString(client.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: nameColor); - } + float iconWidth = iconSize.X / (float)characterColumnWidth; + int xOffset = (int)(jobColumnWidth + characterNameBlock.TextPos.X - GUIStyle.Font.MeasureString(characterNameBlock.Text).X / 2f - paddedFrame.AbsoluteSpacing - iconWidth * paddedFrame.Rect.Width); + permissionIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1f), paddedFrame.RectTransform) { AbsoluteOffset = new Point(xOffset + 2, 0) }, permissionIconSprite) { IgnoreLayoutGroups = true }; + if (client.Character != null && client.Character.IsDead) { @@ -801,7 +864,7 @@ namespace Barotrauma private Sprite GetPermissionIcon(Client client) { - if (GameMain.NetworkMember == null || client == null || !client.HasPermissions) return null; + if (GameMain.NetworkMember == null || client == null || !client.HasPermissions) { return null; } if (client.IsOwner) // Owner cannot be kicked { @@ -898,7 +961,7 @@ namespace Barotrauma GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(walletFrame.RectTransform, 0.9f), walletFrame.RectTransform, anchor: Anchor.Center)); GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.33f), walletLayout.RectTransform), isHorizontal: true); - GUIImage icon = new GUIImage(new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "StoreTradingIcon", scaleToFit: true); + GUIImage icon = new GUIImage(new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "CrewWalletIconLarge", scaleToFit: true); float relativeX = icon.RectTransform.NonScaledSize.X / (float)icon.Parent.RectTransform.NonScaledSize.X; GUILayoutGroup headerTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativeX, 1f), headerLayout.RectTransform), isHorizontal: true) { Stretch = true }; new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.Get("crewwallet.wallet"), font: GUIStyle.LargeFont); @@ -950,7 +1013,7 @@ namespace Barotrauma GUITextBlock rightBalance = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), rightLayout.RectTransform), string.Empty, textAlignment: Alignment.Right) { TextColor = GUIStyle.Red }; GUILayoutGroup centerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), mainLayout.RectTransform, Anchor.Center), childAnchor: Anchor.Center) { IgnoreLayoutGroups = true }; new GUIFrame(new RectTransform(new Vector2(0f, 1f), centerLayout.RectTransform, Anchor.Center), style: "VerticalLine") { IgnoreLayoutGroups = true }; - GUIButton centerButton = new GUIButton(new RectTransform(new Vector2(0.6f), centerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight, anchor: Anchor.Center), style: "GUIButtonTransferArrow"); + GUIButton centerButton = new GUIButton(new RectTransform(new Vector2(1f), centerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight, anchor: Anchor.Center), style: "GUIButtonTransferArrow"); GUILayoutGroup inputLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center); GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), GUINumberInput.NumberType.Int, hidePlusMinusButtons: true) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index de83074a1..3c401566c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -477,6 +477,8 @@ namespace Barotrauma DebugConsole.Init(); + ContentPackageManager.LogEnabledRegularPackageErrors(); + #if !DEBUG && !OSX GameAnalyticsManager.InitIfConsented(); #endif diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs index 87a2c21e7..5d3e959c3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Repairable.cs @@ -59,6 +59,7 @@ namespace Barotrauma.Items.Components public override bool ShouldDrawHUD(Character character) { + if (item.HiddenInGame) { return false; } if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) { return false; } if (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition) { return true; } @@ -222,6 +223,7 @@ namespace Barotrauma.Items.Components partial void UpdateProjSpecific(float deltaTime) { + if (item.HiddenInGame) { return; } if (FakeBrokenTimer > 0.0f) { item.FakeBroken = true; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Hull.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Hull.cs index 665a42380..01bab48a4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Hull.cs @@ -358,9 +358,9 @@ namespace Barotrauma WorldRect.Width, WorldRect.Height); GUI.DrawLine(spriteBatch, - new Vector2(currentHullRect.X, -currentHullRect.Y), - new Vector2(connectedHullRect.X, -connectedHullRect.Y), - GUIStyle.Green, width: 2); + new Vector2(currentHullRect.X, -currentHullRect.Y), + new Vector2(connectedHullRect.X, -connectedHullRect.Y), + GUIStyle.Green, width: 2); } } } @@ -378,7 +378,7 @@ namespace Barotrauma if (section.ColorStrength < 0.01f || section.Color.A < 1) { continue; } - if (DecalManager.GrimeSprites.None()) + if (section.GrimeSprite == null) { GUI.DrawRectangle(spriteBatch, new Vector2(drawOffset.X + rect.X + section.Rect.X, -(drawOffset.Y + rect.Y + section.Rect.Y)), @@ -389,8 +389,7 @@ namespace Barotrauma { Vector2 sectionPos = new Vector2(drawPos.X + section.Rect.Location.X, -(drawPos.Y + section.Rect.Location.Y)); Vector2 randomOffset = new Vector2(section.Noise.X - 0.5f, section.Noise.Y - 0.5f) * 15.0f; - var sprite = DecalManager.GrimeSprites[$"{nameof(GrimeSprite)}{i % DecalManager.GrimeSpriteCount}"].Sprite; - sprite.Draw(spriteBatch, sectionPos + randomOffset, section.GetStrengthAdjustedColor(), scale: 1.25f); + section.GrimeSprite.Draw(spriteBatch, sectionPos + randomOffset, section.GetStrengthAdjustedColor(), scale: 1.25f); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs index 0db354aea..01fb485ac 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/ItemAssemblyPrefab.cs @@ -81,8 +81,8 @@ namespace Barotrauma } Vector2 center = new Vector2((minX + maxX) / 2.0f, (minY + maxY) / 2.0f); if (Submarine.MainSub != null) { center -= Submarine.MainSub.HiddenSubPosition; } - center.X -= center.X % Submarine.GridSize.X; - center.Y -= center.Y % Submarine.GridSize.Y; + center.X -= MathUtils.RoundTowardsClosest(center.X, Submarine.GridSize.X); + center.Y -= MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y); MapEntity.SelectedList.Clear(); assemblyEntities.ForEach(e => MapEntity.AddSelection(e)); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs index a36e4216b..596935d5b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs @@ -143,7 +143,7 @@ namespace Barotrauma Stretch = true, RelativeSpacing = 0.01f }; - new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX")) + new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall") { ToolTip = TextManager.Get("MirrorEntityXToolTip"), OnClicked = (button, data) => @@ -156,7 +156,7 @@ namespace Barotrauma return true; } }; - new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY")) + new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall") { ToolTip = TextManager.Get("MirrorEntityYToolTip"), OnClicked = (button, data) => @@ -169,7 +169,7 @@ namespace Barotrauma return true; } }; - new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite")) + new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite"), style: "GUIButtonSmall") { OnClicked = (button, data) => { @@ -178,7 +178,7 @@ namespace Barotrauma return true; } }; - new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ResetToPrefab")) + new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ResetToPrefab"), style: "GUIButtonSmall") { OnClicked = (button, data) => { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs index 8f48882e1..d6d47fdf7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Submarine.cs @@ -186,9 +186,8 @@ namespace Barotrauma { if (predicate != null) { - if (!predicate(e)) continue; + if (!predicate(e)) { continue; } } - hull.DrawSectionColors(spriteBatch); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/WayPoint.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/WayPoint.cs index e69d0bbb0..1d862e8a6 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/WayPoint.cs @@ -299,7 +299,7 @@ namespace Barotrauma }; new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), spawnTypeContainer.RectTransform), TextManager.Get("SpawnType")); - var button = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton") + var button = new GUIButton(new RectTransform(Vector2.One, spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton") { UserData = -1, OnClicked = ChangeSpawnType @@ -308,7 +308,7 @@ namespace Barotrauma { UserData = "spawntypetext" }; - button = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton") + button = new GUIButton(new RectTransform(Vector2.One, spawnTypeContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton") { UserData = 1, OnClicked = ChangeSpawnType diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs index c59c2d2a6..4ebdb2c37 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs @@ -108,7 +108,7 @@ namespace Barotrauma.Networking { return; } - if (!Permissions.HasFlag(permission)) Permissions |= permission; + if (!Permissions.HasFlag(permission)) { Permissions |= permission; } } public void RemovePermission(ClientPermissions permission) @@ -117,7 +117,7 @@ namespace Barotrauma.Networking { return; } - if (Permissions.HasFlag(permission)) Permissions &= ~permission; + if (Permissions.HasFlag(permission)) { Permissions &= ~permission; } } public bool HasPermission(ClientPermissions permission) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/FileTransfer/FileReceiver.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/FileTransfer/FileReceiver.cs index 8f046b69d..75401668a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/FileTransfer/FileReceiver.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/FileTransfer/FileReceiver.cs @@ -51,6 +51,17 @@ namespace Barotrauma.Networking set; } + public DateTime LastOffsetAckTime + { + get; + private set; + } + + public void RecordOffsetAckTime() + { + LastOffsetAckTime = DateTime.Now; + } + public float BytesPerSecond { get; @@ -91,6 +102,8 @@ namespace Barotrauma.Networking Connection = connection; Status = FileTransferStatus.NotStarted; + + LastOffsetAckTime = DateTime.Now - new TimeSpan(days: 0, hours: 0, minutes: 5, seconds: 0); } public void OpenStream() @@ -214,11 +227,11 @@ namespace Barotrauma.Networking fileName != existingTransfer.FileName) { GameMain.Client.CancelFileTransfer(transferId); - DebugConsole.ThrowError("File transfer error: file transfer initiated with an ID that's already in use"); + DebugConsole.AddWarning("File transfer error: file transfer initiated with an ID that's already in use"); } else //resend acknowledgement packet { - GameMain.Client.UpdateFileTransfer(transferId, existingTransfer.Received, existingTransfer.LastSeen); + GameMain.Client.UpdateFileTransfer(existingTransfer, existingTransfer.Received, existingTransfer.LastSeen); } return; } @@ -285,7 +298,7 @@ namespace Barotrauma.Networking } activeTransfers.Add(newTransfer); - GameMain.Client.UpdateFileTransfer(transferId, 0, 0); //send acknowledgement packet + GameMain.Client.UpdateFileTransfer(newTransfer, 0, 0); //send acknowledgement packet } break; case (byte)FileTransferMessageType.TransferOnSameMachine: @@ -333,7 +346,7 @@ namespace Barotrauma.Networking if (!finishedTransfers.Any(t => t.transferId == transferId)) { GameMain.Client.CancelFileTransfer(transferId); - DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); + DebugConsole.AddWarning("File transfer error: received data without a transfer initiation message"); } return; } @@ -344,7 +357,7 @@ namespace Barotrauma.Networking { activeTransfer.LastSeen = Math.Max(offset, activeTransfer.LastSeen); DebugConsole.Log($"Received {bytesToRead} bytes of the file {activeTransfer.FileName} (ignoring: offset {offset}, waiting for {activeTransfer.Received})"); - GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, activeTransfer.LastSeen); + GameMain.Client.UpdateFileTransfer(activeTransfer, activeTransfer.Received, activeTransfer.LastSeen); return; } activeTransfer.LastSeen = offset; @@ -375,7 +388,7 @@ namespace Barotrauma.Networking return; } - GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, activeTransfer.LastSeen, reliable: activeTransfer.Status == FileTransferStatus.Finished); + GameMain.Client.UpdateFileTransfer(activeTransfer, activeTransfer.Received, activeTransfer.LastSeen, reliable: activeTransfer.Status == FileTransferStatus.Finished); if (activeTransfer.Status == FileTransferStatus.Finished) { activeTransfer.Dispose(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 57ff660c5..08c87776e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -1947,7 +1947,6 @@ namespace Barotrauma.Networking existingClient.Character = null; existingClient.Karma = tc.Karma; existingClient.Muted = tc.Muted; - existingClient.HasPermissions = tc.HasPermissions; existingClient.InGame = tc.InGame; existingClient.IsOwner = tc.IsOwner; existingClient.AllowKicking = tc.AllowKicking; @@ -2493,12 +2492,18 @@ namespace Barotrauma.Networking CancelFileTransfer(transfer.ID); } - public void UpdateFileTransfer(int id, int expecting, int lastSeen, bool reliable = false) + public void UpdateFileTransfer(FileReceiver.FileTransferIn transfer, int expecting, int lastSeen, bool reliable = false) { + if (!reliable && (DateTime.Now - transfer.LastOffsetAckTime).TotalSeconds < 1) + { + return; + } + transfer.RecordOffsetAckTime(); + IWriteMessage msg = new WriteOnlyMessage(); msg.Write((byte)ClientPacketHeader.FILE_REQUEST); msg.Write((byte)FileTransferMessageType.Data); - msg.Write((byte)id); + msg.Write((byte)transfer.ID); msg.Write(expecting); msg.Write(lastSeen); clientPeer.Send(msg, reliable ? DeliveryMethod.Reliable : DeliveryMethod.Unreliable); @@ -2784,7 +2789,9 @@ namespace Barotrauma.Networking public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, float timeOut) { - if (info == null || votingInterface != null) { return; } + if (info == null) { return; } + if (votingInterface != null && votingInterface.VoteRunning) { return; } + votingInterface?.Remove(); votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, timeOut); } #endregion @@ -2792,12 +2799,13 @@ namespace Barotrauma.Networking #region Money Transfer Voting public void ShowMoneyTransferVoteInterface(Client starter, Client from, int amount, Client to, float timeOut) { - if (votingInterface != null) { return; } + if (votingInterface != null && votingInterface.VoteRunning) { return; } if (from == null && to == null) { DebugConsole.ThrowError("Tried to initiate a vote for transferring from null to null!"); return; } + votingInterface?.Remove(); votingInterface = VotingInterface.CreateMoneyTransferVotingInterface(starter, from, to, amount, timeOut); } #endregion diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerInfo.cs index 44e51dfdd..797343808 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerInfo.cs @@ -78,24 +78,6 @@ namespace Barotrauma.Networking get; private set; } = new List(); - - public bool ContentPackagesMatch() - { - //make sure we have all the packages the server requires - if (ContentPackageHashes.Count != ContentPackageWorkshopIds.Count) { return false; } - for (int i = 0; i < ContentPackageWorkshopIds.Count; i++) - { - string hash = ContentPackageHashes[i]; - UInt64 id = ContentPackageWorkshopIds[i]; - if (!GameMain.ServerListScreen.ContentPackagesByHash.ContainsKey(hash)) - { - if (GameMain.ServerListScreen.ContentPackagesByWorkshopId.ContainsKey(id)) { return false; } - if (id == 0) { return false; } - } - } - - return true; - } public void CreatePreviewWindow(GUIFrame frame) { @@ -105,7 +87,8 @@ namespace Barotrauma.Networking var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), ServerName, font: GUIStyle.LargeFont) { - ToolTip = ServerName + ToolTip = ServerName, + CanBeFocused = false }; title.Text = ToolBox.LimitString(title.Text, title.Font, (int)(title.Rect.Width * 0.85f)); @@ -130,7 +113,11 @@ namespace Barotrauma.Networking }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), - TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion)); + TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), + string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion)) + { + CanBeFocused = false + }; bool hidePlaystyleBanner = !PlayStyle.HasValue; if (!hidePlaystyleBanner) @@ -141,15 +128,23 @@ namespace Barotrauma.Networking var playStyleBanner = new GUIImage(new RectTransform(new Point(frame.Rect.Width, (int)(frame.Rect.Width / playStyleBannerAspectRatio)), frame.RectTransform), playStyleBannerSprite, null, true); - var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.06f) }, - TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White, - font: GUIStyle.SmallFont, textAlignment: Alignment.Center, + var playStyleName = new GUITextBlock( + new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) + { RelativeOffset = new Vector2(0.0f, 0.06f) }, + TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), + TextManager.Get("servertag." + playStyle)), textColor: Color.White, + font: GUIStyle.SmallFont, textAlignment: Alignment.Center, color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader"); playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint(); playStyleName.RectTransform.IsFixedSize = true; } + var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), - TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.TopLeft); + TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), + textAlignment: Alignment.TopLeft) + { + CanBeFocused = false + }; serverType.RectTransform.MinSize = new Point(0, (int)(serverType.Rect.Height * 1.5f)); var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), frame.RectTransform)) @@ -270,7 +265,11 @@ namespace Barotrauma.Networking new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), TextManager.Get("ServerListContentPackages"), textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont); - var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), frame.RectTransform)) { ScrollBarVisible = true }; + var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), frame.RectTransform)) + { + ScrollBarVisible = true, + OnSelected = (component, o) => false + }; if (ContentPackageNames.Count == 0) { new GUITextBlock(new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get("Unknown"), textAlignment: Alignment.Center) @@ -282,28 +281,30 @@ namespace Barotrauma.Networking { for (int i = 0; i < ContentPackageNames.Count; i++) { - var packageText = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) }, + var packageText = new GUITickBox( + new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) + { MinSize = new Point(0, 15) }, ContentPackageNames[i]) { - CanBeFocused = false + Enabled = false }; + packageText.Box.Enabled = true; + packageText.TextBlock.Enabled = true; if (i < ContentPackageHashes.Count) { if (ContentPackageManager.AllPackages.Any(contentPackage => contentPackage.Hash.StringRepresentation == ContentPackageHashes[i])) { + packageText.TextColor = GUIStyle.Green; packageText.Selected = true; - continue; } - //workshop download link found - if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0) + else if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0) { - packageText.TextColor = GUIStyle.Yellow; packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", ContentPackageNames[i]); } - else //no package or workshop download link found, tough luck + else //no package or workshop download link found (TODO: update text to say that they could be downloaded through the server) { - packageText.TextColor = GUIStyle.Red; + packageText.TextColor = GameMain.VanillaContent.NameMatches(ContentPackageNames[i]) ? GUIStyle.Red : GUIStyle.Yellow; packageText.ToolTip = TextManager.GetWithVariables("ServerListIncompatibleContentPackage", ("[contentpackage]", ContentPackageNames[i]), ("[hash]", ContentPackageHashes[i])); } @@ -342,7 +343,7 @@ namespace Barotrauma.Networking } if (ContentPackageNames.Count > 0) { - tags.Add(ContentPackageNames.Count > 1 || ContentPackageNames[0] != GameMain.VanillaContent?.Name ? "modded.true" : "modded.false"); + tags.Add(ContentPackageNames.Count > 1 || !GameMain.VanillaContent.NameMatches(ContentPackageNames[0]) ? "modded.true" : "modded.false"); } return tags; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Particles/Particle.cs b/Barotrauma/BarotraumaClient/ClientSource/Particles/Particle.cs index dbe56f22d..fa77bcd3e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Particles/Particle.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Particles/Particle.cs @@ -336,7 +336,7 @@ namespace Barotrauma.Particles Hull collidedHull = Hull.FindHull(position); if (collidedHull != null) { - if (prefab.DeleteOnCollision) return UpdateResult.Delete; + if (prefab.DeleteOnCollision) { return UpdateResult.Delete; } OnWallCollisionOutside(collidedHull); } } @@ -346,20 +346,10 @@ namespace Barotrauma.Particles Vector2 collisionNormal = Vector2.Zero; if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height) { - if (prefab.DeleteOnCollision) - { - OnCollision?.Invoke(position, currentHull); - return UpdateResult.Delete; - } collisionNormal = new Vector2(0.0f, 1.0f); } else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y) { - if (prefab.DeleteOnCollision) - { - OnCollision?.Invoke(position, currentHull); - return UpdateResult.Delete; - } collisionNormal = new Vector2(0.0f, -1.0f); } @@ -379,18 +369,21 @@ namespace Barotrauma.Particles break; } + if (prefab.DeleteOnCollision && !gapFound) + { + OnCollision?.Invoke(position, currentHull); + return UpdateResult.Delete; + } handleCollision(gapFound, collisionNormal); } collisionNormal = Vector2.Zero; if (velocity.X < 0.0f && position.X - prefab.CollisionRadius * size.X < hullRect.X) { - if (prefab.DeleteOnCollision) { return UpdateResult.Delete; } collisionNormal = new Vector2(1.0f, 0.0f); } else if (velocity.X > 0.0f && position.X + prefab.CollisionRadius * size.X > hullRect.Right) { - if (prefab.DeleteOnCollision) { return UpdateResult.Delete; } collisionNormal = new Vector2(-1.0f, 0.0f); } @@ -408,7 +401,11 @@ namespace Barotrauma.Particles gapFound = true; break; } - + if (prefab.DeleteOnCollision && !gapFound) + { + OnCollision?.Invoke(position, currentHull); + return UpdateResult.Delete; + } handleCollision(gapFound, collisionNormal); } @@ -512,7 +509,7 @@ namespace Barotrauma.Particles { Rectangle hullRect = collisionHull.WorldRect; - Vector2 center = new Vector2(hullRect.X + hullRect.Width /2, hullRect.Y - hullRect.Height / 2); + Vector2 center = new Vector2(hullRect.X + hullRect.Width / 2, hullRect.Y - hullRect.Height / 2); if (position.Y < center.Y) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs index 7108479ce..9e690bb7b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs @@ -17,6 +17,7 @@ namespace Barotrauma Hull.EditFire = false; Hull.EditWater = false; #endif + HumanAIController.DisableCrewAI = false; } protected virtual void DeselectEditorSpecific() { } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EditorNode.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EditorNode.cs index 3b8f0f22b..5383d7614 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EditorNode.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EditorNode.cs @@ -131,7 +131,7 @@ namespace Barotrauma } else { - connection.OverrideValue = Convert.ChangeType(overrideValue, type); + connection.OverrideValue = EventEditorScreen.ChangeType(overrideValue, type); } } } @@ -513,7 +513,7 @@ namespace Barotrauma } else { - newNode.Value = Convert.ChangeType(value, type); + newNode.Value = EventEditorScreen.ChangeType(value, type); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs index 395391803..2e450a813 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs @@ -440,7 +440,7 @@ namespace Barotrauma } else { - connection.OverrideValue = Convert.ChangeType(attribute.Value, connection.ValueType); + connection.OverrideValue = ChangeType(attribute.Value, connection.ValueType); } } } @@ -524,6 +524,18 @@ namespace Barotrauma GuiFrame.AddToGUIUpdateList(); } + public static object? ChangeType(string value, Type type) + { + if (type == typeof(Identifier)) + { + return value.ToIdentifier(); + } + else + { + return Convert.ChangeType(value, type); + } + } + private XElement? ExportXML() { XElement mainElement = new XElement("ScriptedEvent", new XAttribute("identifier", projectName.RemoveWhitespace().ToLowerInvariant())); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs index b9aed65dd..146f41f13 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs @@ -883,12 +883,17 @@ namespace Barotrauma private void SerializeAll() { + IEnumerable packages = ContentPackageManager.LocalPackages; +#if DEBUG + packages = packages.Union(ContentPackageManager.VanillaCorePackage.ToEnumerable()); +#endif + System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, NewLineOnAttributes = true }; - foreach (var configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles())) + foreach (var configFile in packages.SelectMany(p => p.GetFiles())) { XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); if (doc == null) { continue; } @@ -922,7 +927,7 @@ namespace Barotrauma } } - foreach (var configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles())) + foreach (var configFile in packages.SelectMany(p => p.GetFiles())) { XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); if (doc == null) { continue; } @@ -957,7 +962,7 @@ namespace Barotrauma } settings.NewLineOnAttributes = false; - foreach (var configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles())) + foreach (var configFile in packages.SelectMany(p => p.GetFiles())) { XDocument doc = XMLExtensions.TryLoadXml(configFile.Path); if (doc == null) { continue; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs index c07116c26..6e278ac0a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs @@ -8,7 +8,6 @@ using Barotrauma.Networking; using Barotrauma.Steam; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Steamworks.Data; using Color = Microsoft.Xna.Framework.Color; using ServerContentPackage = Barotrauma.Networking.ClientPeer.ServerContentPackage; @@ -164,7 +163,7 @@ namespace Barotrauma => wp.SteamWorkshopId != mp.WorkshopId)) .Select(mp => mp.WorkshopId) .ToArray(); - if (missingIds.Any()) + if (missingIds.Any() && SteamManager.IsInitialized) { buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), innerLayout.RectTransform), isHorizontal: true); buttonContainerSpacing(0.15f); @@ -208,7 +207,7 @@ namespace Barotrauma { if (currentDownload == p) { - FileReceiver.FileTransferIn? getTransfer() => GameMain.Client.FileReceiver.ActiveTransfers.FirstOrDefault(t => t.FileType == FileTransferType.Mod); + FileReceiver.FileTransferIn? getTransfer() => GameMain.Client?.FileReceiver.ActiveTransfers.FirstOrDefault(t => t.FileType == FileTransferType.Mod); if (downloadProgress.GetAnyChild() is null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index b3949ac46..a0cb2b9f4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -2298,7 +2298,7 @@ namespace Barotrauma text: selectedClient.Name, font: GUIStyle.LargeFont); nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, (int)(nameText.Rect.Width * 0.95f)); - if (hasManagePermissions) + if (hasManagePermissions && !selectedClient.IsOwner) { PlayerFrame.UserData = selectedClient; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs index bd2b9b4c9..1bf91b69e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs @@ -557,7 +557,9 @@ namespace Barotrauma }; serverPreview = new GUIListBox(new RectTransform(Vector2.One, serverPreviewContainer.RectTransform, Anchor.Center)) { - Padding = Vector4.One * 10 * GUI.Scale + Padding = Vector4.One * 10 * GUI.Scale, + HoverCursor = CursorState.Default, + OnSelected = (component, o) => false }; // Spacing @@ -915,12 +917,7 @@ namespace Barotrauma { case "ServerListCompatible": bool? s1Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s1.GameVersion); - if (!s1.ContentPackageHashes.Any()) { s1Compatible = null; } - if (s1Compatible.HasValue) { s1Compatible = s1Compatible.Value && s1.ContentPackagesMatch(); }; - bool? s2Compatible = NetworkMember.IsCompatible(GameMain.Version.ToString(), s2.GameVersion); - if (!s2.ContentPackageHashes.Any()) { s2Compatible = null; } - if (s2Compatible.HasValue) { s2Compatible = s2Compatible.Value && s2.ContentPackagesMatch(); }; //convert to int to make sorting easier //1 Compatible @@ -1028,8 +1025,7 @@ namespace Barotrauma foreach (GUIComponent child in serverList.Content.Children) { - if (!(child.UserData is ServerInfo)) continue; - ServerInfo serverInfo = (ServerInfo)child.UserData; + if (!(child.UserData is ServerInfo serverInfo)) { continue; } Version remoteVersion = null; if (!string.IsNullOrEmpty(serverInfo.GameVersion)) @@ -1047,8 +1043,7 @@ namespace Barotrauma else { bool incompatible = - (serverInfo.ContentPackageHashes.Any() && !serverInfo.ContentPackagesMatch()) || - (remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion)); + remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion); var karmaFilterPassed = filterKarmaValue == TernaryOption.Any|| (filterKarmaValue == TernaryOption.Enabled) == serverInfo.KarmaEnabled; var friendlyFireFilterPassed = filterFriendlyFireValue == TernaryOption.Any || (filterFriendlyFireValue == TernaryOption.Enabled) == serverInfo.FriendlyFireEnabled; @@ -1798,8 +1793,7 @@ namespace Barotrauma { CanBeFocused = false, Selected = - (NetworkMember.IsCompatible(GameMain.Version.ToString(), serverInfo.GameVersion) ?? true) && - serverInfo.ContentPackagesMatch(), + (NetworkMember.IsCompatible(GameMain.Version.ToString(), serverInfo.GameVersion) ?? true), UserData = "compatible" }; @@ -1826,9 +1820,9 @@ namespace Barotrauma if (serverInfo.ContentPackageNames.Any()) { - if (serverInfo.ContentPackageNames.Any(cp => !cp.Equals(GameMain.VanillaContent.Name, StringComparison.OrdinalIgnoreCase))) + if (serverInfo.ContentPackageNames.Any(p => !GameMain.VanillaContent.NameMatches(p))) { - serverName.TextColor = new Color(219, 125, 217); + serverName.TextColor = GUIStyle.ModdedServerColor; } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index 4d72c0d65..6e6826302 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -2841,6 +2841,7 @@ namespace Barotrauma private void CreateLoadScreen() { CloseItem(); + SubmarineInfo.RefreshSavedSubs(); SetMode(Mode.Default); loadFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null) @@ -3162,7 +3163,6 @@ namespace Barotrauma } sub.Dispose(); - SubmarineInfo.RefreshSavedSubs(); CreateLoadScreen(); } catch (Exception e) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs index 823439ace..977add7cd 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs @@ -168,7 +168,10 @@ namespace Barotrauma { var dropdown = new GUIDropDown(NewItemRectT(parent)); values.ForEach(v => dropdown.AddItem(text: textFunc(v), userData: v, toolTip: tooltipFunc?.Invoke(v) ?? null)); - dropdown.Select(values.IndexOf(currentValue)); + int childIndex = values.IndexOf(currentValue); + dropdown.Select(childIndex); + dropdown.ListBox.ForceLayoutRecalculation(); + dropdown.ListBox.ScrollToElement(dropdown.ListBox.Content.GetChild(childIndex), playSound: false); dropdown.OnSelected = (dd, obj) => { setter((T)obj); @@ -231,6 +234,8 @@ namespace Barotrauma GameMain.GraphicsDeviceManager.GraphicsDevice.Adapter.SupportedDisplayModes .Where(m => m.Format == SurfaceFormat.Color) .Select(m => (m.Width, m.Height)) + .Where(m => m.Width >= GameSettings.Config.GraphicsSettings.MinSupportedResolution.X + && m.Height >= GameSettings.Config.GraphicsSettings.MinSupportedResolution.Y) .ToList(); var currentResolution = (unsavedConfig.Graphics.Width, unsavedConfig.Graphics.Height); if (!supportedResolutions.Contains(currentResolution)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs index ac40b98bd..7fe005bb5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs @@ -372,13 +372,10 @@ namespace Barotrauma.Sounds } var newSound = new OggSound(this, filePath, stream, xElement: element); - if (newSound != null) - { - newSound.BaseGain = element.GetAttributeFloat("volume", 1.0f); - float range = element.GetAttributeFloat("range", 1000.0f); - newSound.BaseNear = range * 0.4f; - newSound.BaseFar = range; - } + newSound.BaseGain = element.GetAttributeFloat("volume", 1.0f); + float range = element.GetAttributeFloat("range", 1000.0f); + newSound.BaseNear = range * 0.4f; + newSound.BaseFar = range; lock (loadedSounds) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs index ee74766d9..cdd692671 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs @@ -178,6 +178,7 @@ namespace Barotrauma SoundChannel chn = waterAmbienceChannels.FirstOrDefault(c => c.Sound == sound); if (chn is null || !chn.IsPlaying) { + if (volume < 0.01f) { return; } if (!(chn is null)) { waterAmbienceChannels.Remove(chn); } chn = sound.Play(volume, "waterambience"); chn.Looping = true; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs index 14b0b68e2..9eab19935 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs @@ -202,6 +202,11 @@ namespace Barotrauma { Sound?.Dispose(); Sound = null; } + + ~SoundPrefab() + { + Dispose(); + } } [TagNames("damagesound")] diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/Workshop.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/Workshop.cs index 3a90d6ea1..37a04c2a0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/Workshop.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/Workshop.cs @@ -270,8 +270,9 @@ namespace Barotrauma.Steam .ToHashSet(); toUninstall.Select(p => p.Dir).ForEach(d => Directory.Delete(d)); CrossThread.RequestExecutionOnMainThread(() => ContentPackageManager.WorkshopPackages.Refresh()); + var installWaiter = WaitForInstall(workshopItem); DownloadModThenEnqueueInstall(workshopItem); - await WaitForInstall(workshopItem); + await installWaiter; } public static async Task WaitForInstall(Steamworks.Ugc.Item item) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs index 5b87ab120..484fe3182 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Linq; using Barotrauma.Extensions; using Microsoft.Xna.Framework; @@ -39,6 +40,11 @@ namespace Barotrauma.Steam CanBeFocused = false, UserData = p }; + if (p.Errors.Any()) + { + CreateModErrorInfo(p, regularBox, regularBox); + regularBox.CanBeFocused = true; + } } filterBox = CreateSearchBox(mainLayout, width: 1.0f); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs index 4e01d5ece..d886a30dc 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs @@ -20,10 +20,10 @@ namespace Barotrauma.Steam Publish } - protected readonly GUILayoutGroup tabber; - protected readonly Dictionary tabContents; + private readonly GUILayoutGroup tabber; + private readonly Dictionary tabContents; - protected readonly GUIFrame contentFrame; + private readonly GUIFrame contentFrame; private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected"); @@ -40,6 +40,8 @@ namespace Barotrauma.Steam private readonly GUIListBox popularModsList; private readonly GUIListBox selfModsList; + private uint memSubscribedModCount = 0; + public MutableWorkshopMenu(GUIFrame parent) : base(parent) { var mainLayout @@ -50,6 +52,9 @@ namespace Barotrauma.Steam tabContents = new Dictionary(); contentFrame = new GUIFrame(new RectTransform((1.0f, 0.95f), mainLayout.RectTransform), style: null); + + new GUICustomComponent(new RectTransform(Vector2.Zero, mainLayout.RectTransform), + onUpdate: (f, component) => UpdateSubscribedModInstalls()); CreateInstalledModsTab( out enabledCoreDropdown, @@ -64,6 +69,38 @@ namespace Barotrauma.Steam SelectTab(Tab.InstalledMods); } + private void UpdateSubscribedModInstalls() + { + if (!SteamManager.IsInitialized) { return; } + + uint numSubscribedMods = Steamworks.SteamUGC.NumSubscribedItems; + if (numSubscribedMods == memSubscribedModCount) { return; } + memSubscribedModCount = numSubscribedMods; + + var subscribedIds = Steamworks.SteamUGC.GetSubscribedItems().ToHashSet(); + var installedIds = ContentPackageManager.WorkshopPackages.Select(p => p.SteamWorkshopId).ToHashSet(); + foreach (var id in subscribedIds.Where(id2 => !installedIds.Contains(id2))) + { + Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id); + if (!item.IsDownloading && !SteamManager.Workshop.IsInstalling(item)) + { + SteamManager.Workshop.DownloadModThenEnqueueInstall(item); + } + } + + TaskPool.Add("RemoveUnsubscribedItems", SteamManager.Workshop.GetPublishedItems(), t => + { + if (!t.TryGetResult(out ISet publishedItems)) { return; } + + var allRequiredInstalled = subscribedIds.Union(publishedItems.Select(it => it.Id)).ToHashSet(); + foreach (var id in installedIds.Where(id2 => !allRequiredInstalled.Contains(id2))) + { + Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id); + SteamManager.Workshop.Uninstall(item); + } + }); + } + private void SwitchContent(GUIFrame newContent) { contentFrame.Children.ForEach(c => c.Visible = false); @@ -462,6 +499,10 @@ namespace Barotrauma.Steam { CanBeFocused = false }; + if (mod.Errors.Any()) + { + CreateModErrorInfo(mod, modFrame, modName); + } if (ContentPackageManager.LocalPackages.Contains(mod)) { var editButton = new GUIButton(new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "", @@ -593,6 +634,7 @@ namespace Barotrauma.Steam ContentPackageManager.EnabledPackages.SetRegular(enabledRegularModsList.Content.Children .Select(c => c.UserData as RegularPackage).OfType().ToArray()); PopulateInstalledModLists(forceRefreshEnabled: true, refreshDisabled: true); + ContentPackageManager.LogEnabledRegularPackageErrors(); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs index 78a08a529..692bc61b9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs @@ -129,5 +129,20 @@ namespace Barotrauma.Steam }; return searchBox; } + + protected void CreateModErrorInfo(ContentPackage mod, GUIComponent uiElement, GUITextBlock nameText) + { + if (mod.Errors.Any()) + { + const int maxErrorsToShow = 5; + nameText.TextColor = GUIStyle.Red; + uiElement.ToolTip = + TextManager.GetWithVariable("contentpackagehaserrors", "[packagename]", mod.Name) + '\n' + string.Join('\n', mod.Errors.Take(maxErrorsToShow).Select(e => e.error)); + if (mod.Errors.Count() > maxErrorsToShow) + { + uiElement.ToolTip += '\n' + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (mod.Errors.Count() - maxErrorsToShow).ToString()); + } + } + } } } diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index fceac628c..a49d98a1d 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index cc6dc075d..b8ef2e701 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 77da65846..c4137dcdf 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 0abfeb8ce..6e72c14b2 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 3c9b6adea..989b6a1f4 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs index 532d46b51..4af9addf0 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameMain.cs @@ -106,6 +106,7 @@ namespace Barotrauma GameModePreset.Init(); ContentPackageManager.Init().Consume(); + ContentPackageManager.LogEnabledRegularPackageErrors(); SubmarineInfo.RefreshSavedSubs(); diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index cef019215..ff0fc0357 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -917,18 +917,17 @@ namespace Barotrauma TransferMoney(wallet); break; case None _: - if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) + if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { if (transfer.Receiver is Some { Value: var receiverId } && receiverId == sender.CharacterID) { GameMain.Server?.Voting.StartTransferVote(sender, null, transfer.Amount, sender); + GameServer.Log($"{sender.Name} started a vote to transfer {transfer.Amount} mk from the bank.", ServerLog.MessageType.Money); } - return; - } - else - { - TransferMoney(Bank); + return; } + + TransferMoney(Bank); break; } @@ -943,9 +942,11 @@ namespace Barotrauma if (wallet is InvalidWallet) { return; } wallet.Give(transfer.Amount); + GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {wallet.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money); break; case None _: Bank.Give(transfer.Amount); + GameServer.Log($"{sender.Name} transferred {transfer.Amount} mk to {Bank.GetOwnerLogName()} from {from.GetOwnerLogName()}.", ServerLog.MessageType.Money); break; } } @@ -965,6 +966,7 @@ namespace Barotrauma Character targetCharacter = Character.CharacterList.FirstOrDefault(c => c.ID == update.Target); targetCharacter?.Wallet.SetRewardDistribution(update.NewRewardDistribution); + GameServer.Log($"{sender.Name} changed the salary of {targetCharacter?.Name ?? "the bank"} to {update.NewRewardDistribution}%.", ServerLog.MessageType.Money); } public void ServerReadCrew(IReadMessage msg, Client sender) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs index 408c3ae61..ae0f4c4c6 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs @@ -46,7 +46,7 @@ namespace Barotrauma.Networking { //setting a wait timer means that network conditions //aren't ideal, slow down the packet rate - PacketsPerUpdate = Math.Max(PacketsPerUpdate / 2.0f, 1.0f); + PacketsPerUpdate = Math.Max(PacketsPerUpdate / 4.0f, 1.0f); } waitTimer = value; } @@ -130,7 +130,7 @@ namespace Barotrauma.Networking public FileSender(ServerPeer serverPeer, int mtu) { peer = serverPeer; - chunkLen = mtu - 100; + chunkLen = mtu - 200; activeTransfers = new List(); } @@ -197,11 +197,8 @@ namespace Barotrauma.Networking foreach (FileTransferOut transfer in activeTransfers) { transfer.WaitTimer -= deltaTime; - for (int i = 0; i < 10; i++) - { - if (transfer.WaitTimer > 0.0f) { break; } - Send(transfer); - } + if (transfer.WaitTimer > 0.0f) { continue; } + Send(transfer); } if (numRemoved > 0 || endedTransfers.Count > 0) @@ -281,7 +278,7 @@ namespace Barotrauma.Networking if (transfer.SentOffset >= transfer.Data.Length) { transfer.SentOffset = transfer.KnownReceivedOffset; - transfer.WaitTimer = 0.5f; + transfer.WaitTimer = 1.0f; } peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable, compressPastThreshold: false); @@ -356,7 +353,7 @@ namespace Barotrauma.Networking matchingTransfer.SentOffset >= matchingTransfer.Data.Length) { matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset; - matchingTransfer.WaitTimer = 0.5f; + matchingTransfer.WaitTimer = 1.0f; } if (matchingTransfer.KnownReceivedOffset >= matchingTransfer.Data.Length) @@ -364,6 +361,7 @@ namespace Barotrauma.Networking matchingTransfer.Status = FileTransferStatus.Finished; } } + return; } FileTransferType fileType = (FileTransferType)inc.ReadByte(); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs index 985bcdf3c..f8a129f51 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs @@ -91,13 +91,16 @@ namespace Barotrauma private void StartSubmarineVote(SubmarineInfo subInfo, VoteType voteType, Client sender) { + if (ActiveVote == null) + { + sender.SetVote(voteType, 2); + } var subVote = new SubmarineVote( sender, subInfo, voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0, voteType); StartOrEnqueueVote(subVote); - sender.SetVote(voteType, 2); GameMain.Server.UpdateVoteStatus(checkActiveVote: false); } @@ -127,13 +130,17 @@ namespace Barotrauma if (pendingVotes.Any()) { ActiveVote = pendingVotes.Dequeue(); + ActiveVote.VoteStarter?.SetVote(ActiveVote.VoteType, 2); } } public void StartTransferVote(Client starter, Client from, int transferAmount, Client to) { + if (ActiveVote == null) + { + starter.SetVote(VoteType.TransferMoney, 2); + } StartOrEnqueueVote(new TransferVote(starter, from, transferAmount, to)); - starter.SetVote(VoteType.TransferMoney, 2); GameMain.Server.UpdateVoteStatus(checkActiveVote: false); } diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index a5861e794..a56b1996b 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.6.0 + 0.17.7.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index e3f0db038..08fa162ea 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -62,9 +62,9 @@ namespace Barotrauma private float enemycheckTimer; /// - /// How far other characters can hear reports done by this character (e.g. reports for fires, intruders). + /// How far other characters can hear reports done by this character (e.g. reports for fires, intruders). Defaults to infinity. /// - public float ReportRange { get; set; } + public float ReportRange { get; set; } = float.PositiveInfinity; private float _aimSpeed = 1; public float AimSpeed @@ -166,7 +166,6 @@ namespace Barotrauma objectiveManager = new AIObjectiveManager(c); reactTimer = GetReactionTime(); SortTimer = Rand.Range(0f, sortObjectiveInterval); - ReportRange = Character.IsOnPlayerTeam ? float.PositiveInfinity : 1000; } public override void Update(float deltaTime) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs index 4abcd54ee..0c46fca5d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs @@ -269,6 +269,18 @@ namespace Barotrauma if (!character.AnimController.InWater || character.Submarine != null) { return; } if (CurrentPath == null || CurrentPath.Unreachable || CurrentPath.Finished) { return; } if (CurrentPath.CurrentIndex < 0 || CurrentPath.CurrentIndex >= CurrentPath.Nodes.Count - 1) { return; } + var lastNode = CurrentPath.Nodes.Last(); + Submarine targetSub = lastNode.Submarine; + if (targetSub != null) + { + float subSize = Math.Max(targetSub.Borders.Size.X, targetSub.Borders.Size.Y) / 2; + float margin = 500; + if (Vector2.DistanceSquared(character.WorldPosition, targetSub.WorldPosition) < MathUtils.Pow2(subSize + margin)) + { + // Don't skip nodes when close to the target submarine. + return; + } + } // Check if we could skip ahead to NextNode when the character is swimming and using waypoints outside. // Do this to optimize the old path before creating and evaluating a new path. // In general, this is to avoid behavior where: @@ -280,7 +292,7 @@ namespace Barotrauma { var waypoint = CurrentPath.Nodes[i]; float directDistance = Vector2.DistanceSquared(character.WorldPosition, waypoint.WorldPosition); - if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null) + if (directDistance > MathUtils.Pow2(pathDistance) || !character.CanSeeTarget(waypoint)) { pathDistance -= CurrentPath.GetLength(startIndex: i - 1, endIndex: i); continue; @@ -336,6 +348,7 @@ namespace Barotrauma return Vector2.Zero; } Vector2 pos = host.WorldPosition; + Vector2 diff = currentPath.CurrentNode.WorldPosition - pos; bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater; // Only humanoids can climb ladders bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands; @@ -346,7 +359,7 @@ namespace Barotrauma } Ladder nextLadder = GetNextLadder(); var ladders = currentLadder ?? nextLadder; - bool useLadders = canClimb && ladders != null && (!isDiving || Math.Abs(steering.X) < 0.1f && steering.Y > 1); + bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1); if (useLadders && character.SelectedConstruction != ladders.Item) { if (character.CanInteractWith(ladders.Item)) @@ -374,7 +387,6 @@ namespace Barotrauma } if (character.IsClimbing && useLadders) { - Vector2 diff = currentPath.CurrentNode.WorldPosition - pos; bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent; if (nextLadderSameAsCurrent || currentLadder != null && nextLadder != null && Math.Abs(currentLadder.Item.Position.X - nextLadder.Item.Position.X) < 50) { @@ -398,7 +410,7 @@ namespace Barotrauma else if (nextLadder != null && !nextLadderSameAsCurrent) { // Try to change the ladder (hatches between two submarines) - if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition)) + if (character.SelectedConstruction != nextLadder.Item && character.CanInteractWith(nextLadder.Item)) { if (nextLadder.Item.TryInteract(character, forceSelectKey: true)) { @@ -406,7 +418,7 @@ namespace Barotrauma } } } - if (isAboveFloor || nextLadderSameAsCurrent) + if (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10) { NextNode(!doorsChecked); } @@ -484,7 +496,7 @@ namespace Barotrauma { return Vector2.Zero; } - return ConvertUnits.ToSimUnits(currentPath.CurrentNode.WorldPosition - pos); + return ConvertUnits.ToSimUnits(diff); } private void NextNode(bool checkDoors) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs index f93ededde..bdc673490 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -212,10 +212,14 @@ namespace Barotrauma protected override bool CheckObjectiveSpecific() { - if (sqrDistance > maxDistance * maxDistance) + if (character.Submarine == null || character.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { - // The target escaped from us. - return true; + // Can't lose the target in friendly outposts. + if (sqrDistance > maxDistance * maxDistance) + { + // The target escaped from us. + return true; + } } return IsEnemyDisabled || (AllowCoolDown && coolDownTimer <= 0); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs index c66746f9a..ba7a674fb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs @@ -101,7 +101,7 @@ namespace Barotrauma { if (InWater || !CanWalk) { - return TargetMovement.LengthSquared() > SwimSlowParams.MovementSpeed; + return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed); } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs index e524f3a87..d47bbf405 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs @@ -302,7 +302,7 @@ namespace Barotrauma } // used for talents/ability conditions - public Item SourceItem { get; } + public Item SourceItem { get; set; } public List GetMultipliedAfflictions(float multiplier) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index 5182ff0b6..57df4275c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -308,6 +308,10 @@ namespace Barotrauma public bool IsHumanoid => Params.Humanoid; public bool IsHusk => Params.Husk; + public bool IsMale => info?.IsMale ?? false; + + public bool IsFemale => info?.IsFemale ?? false; + public string BloodDecalName => Params.BloodDecal; public bool CanSpeak @@ -557,10 +561,11 @@ namespace Barotrauma #if CLIENT CharacterHealth.SetHealthBarVisibility(value == null); #elif SERVER - if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet }) + if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet } && balance > 0) { Wallet.Give(balance); grabbedWallet.Deduct(balance); + GameServer.Log($"{Name} grabbed {value.Name}'s body and received {grabbedWallet.Balance} mk.", ServerLog.MessageType.Money); } #endif } @@ -3587,21 +3592,8 @@ namespace Barotrauma float attackImpulse = attack.TargetImpulse + attack.TargetForce * attack.ImpactMultiplier * deltaTime; - AbilityAttackData attackData = new AbilityAttackData(attack, this); - if (attacker != null) - { - attackData.Attacker = attacker; - attacker.CheckTalents(AbilityEffectType.OnAttack, attackData); - CheckTalents(AbilityEffectType.OnAttacked, attackData); - attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.AttackMultiplier); - if (attacker.TeamID == TeamID) - { - attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.TeamAttackMultiplier); - } - } - + AbilityAttackData attackData = new AbilityAttackData(attack, this, attacker); IEnumerable attackAfflictions; - if (attackData.Afflictions != null) { attackAfflictions = attackData.Afflictions.Union(attack.Afflictions.Keys); @@ -4916,10 +4908,21 @@ namespace Barotrauma public Character Character { get; set; } public Character Attacker { get; set; } - public AbilityAttackData(Attack sourceAttack, Character character) + public AbilityAttackData(Attack sourceAttack, Character target, Character attacker) { SourceAttack = sourceAttack; - Character = character; + Character = target; + if (attacker != null) + { + Attacker = attacker; + attacker.CheckTalents(AbilityEffectType.OnAttack, this); + target.CheckTalents(AbilityEffectType.OnAttacked, this); + DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.AttackMultiplier); + if (attacker.TeamID == target.TeamID) + { + DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.TeamAttackMultiplier); + } + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs index df1cede68..1d384e608 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs @@ -130,17 +130,22 @@ namespace Barotrauma head = value; HeadSprite = null; AttachmentSprites = null; + IsMale = value.Preset?.TagSet?.Contains("Male".ToIdentifier()) ?? false; + IsFemale = value.Preset?.TagSet?.Contains("Female".ToIdentifier()) ?? false; } } } + public bool IsMale { get; private set; } + + public bool IsFemale { get; private set; } + public CharacterInfoPrefab Prefab => CharacterPrefab.Prefabs[SpeciesName].CharacterInfoPrefab; public class HeadPreset : ISerializableEntity { private readonly CharacterInfoPrefab characterInfoPrefab; public Identifier MenuCategory => TagSet.First(t => characterInfoPrefab.VarTags[characterInfoPrefab.MenuCategoryVar].Contains(t)); - public ImmutableHashSet TagSet { get; private set; } [Serialize("", IsPropertySaveable.No)] diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ContentFile.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ContentFile.cs index 35865bb05..4f853ba01 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ContentFile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ContentFile.cs @@ -71,8 +71,8 @@ namespace Barotrauma public static Result CreateFromXElement(ContentPackage contentPackage, XElement element) { - static Result fail(string error) - => Result.Failure(error); + static Result fail(string error, string? stackTrace = null) + => Result.Failure(error, stackTrace); Identifier elemName = element.NameAsIdentifier(); var type = Types.FirstOrDefault(t => t.Names.Contains(elemName)); @@ -99,10 +99,10 @@ namespace Barotrauma } catch (Exception e) { - return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": {e.Message}\n{e.StackTrace.CleanupStackTrace()}"); + return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": {e.Message}", e.StackTrace.CleanupStackTrace()); } } - + protected ContentFile(ContentPackage contentPackage, ContentPath path) { ContentPackage = contentPackage; diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/GenericPrefabFile.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/GenericPrefabFile.cs index 3523bb443..e59272553 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/GenericPrefabFile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/GenericPrefabFile.cs @@ -34,7 +34,15 @@ namespace Barotrauma else if (MatchesSingular(elemName)) { T prefab = CreatePrefab(parentElement); - prefabs.Add(prefab, overriding); + try + { + prefabs.Add(prefab, overriding); + } + catch + { + prefab.Dispose(); //clean up before rethrowing, since some prefab types might lock resources + throw; + } } else if (MatchesPlural(elemName)) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs index 586da6857..35e6b6436 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackage/ContentPackage.cs @@ -1,6 +1,6 @@ #nullable enable using Barotrauma.Extensions; -using Microsoft.Xna.Framework; +using Barotrauma.Steam; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -9,7 +9,6 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; -using Barotrauma.Steam; namespace Barotrauma { @@ -39,7 +38,7 @@ namespace Barotrauma public readonly DateTime? InstallTime; public readonly ImmutableArray Files; - public readonly ImmutableArray Errors; + public readonly ImmutableArray<(string error, string? stackTrace)> Errors; public async Task IsUpToDate() { @@ -92,7 +91,7 @@ namespace Barotrauma Errors = fileResults .OfType>() - .Select(f => f.Error) + .Select(f => (f.Error, f.StackTrace)) .ToImmutableArray(); HasMultiplayerSyncedContent = Files.Any(f => !f.NotSyncedInMultiplayer); @@ -304,5 +303,24 @@ namespace Barotrauma } return path == LocalModsDir; } + + public void LogErrors() + { + if (Errors.Any()) + { + DebugConsole.AddWarning( + $"The following errors occurred while loading the content package\"{Name}\". The package might not work correctly.\n" + + string.Join('\n', Errors.Select(e => errorToStr(e.error, e.stackTrace)))); + static string errorToStr(string error, string? stackTrace) + { + string str = error; + if (stackTrace != null) + { + str += '\n' + stackTrace; + } + return str; + } + } + } } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs index add517a0c..533690a7a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs @@ -430,9 +430,9 @@ namespace Barotrauma public static void LoadVanillaFileList() { VanillaCorePackage = new CorePackage(XDocument.Load(VanillaFileList), VanillaFileList); - foreach (string error in VanillaCorePackage.Errors) + foreach ((string error, string? stackTrace) in VanillaCorePackage.Errors) { - DebugConsole.ThrowError(error); + DebugConsole.ThrowError(error + (stackTrace == null ? string.Empty : '\n' + stackTrace)); } } @@ -469,8 +469,17 @@ namespace Barotrauma } var corePackageElement = contentPackagesElement.GetChildElement(CorePackageElementName); - enabledCorePackage = findPackage(CorePackages, corePackageElement) ?? VanillaCorePackage!; - + var configEnabledCorePackage = findPackage(CorePackages, corePackageElement); + if (configEnabledCorePackage == null) + { + string packageStr = corePackageElement.GetAttributeString("name", null) ?? corePackageElement.GetAttributeStringUnrestricted("path", "UNKNOWN"); + DebugConsole.ThrowError($"Could not find the selected core package \"{packageStr}\". Switching to the \"{enabledCorePackage.Name}\" package."); + } + else + { + enabledCorePackage = configEnabledCorePackage; + } + var regularPackagesElement = contentPackagesElement.GetChildElement(RegularPackagesElementName); if (regularPackagesElement != null) { @@ -499,5 +508,13 @@ namespace Barotrauma yield return new LoadProgress(1.0f); } + + public static void LogEnabledRegularPackageErrors() + { + foreach (var p in EnabledPackages.Regular) + { + p.LogErrors(); + } + } } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index f9b027f33..077da45bd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -1562,17 +1562,24 @@ namespace Barotrauma commands.Add(new Command("money", "money [amount] [character]: Gives the specified amount of money to the crew when a campaign is active.", args => { if (args.Length == 0) { return; } - if (GameMain.GameSession?.GameMode is CampaignMode campaign) + + if (!(GameMain.GameSession?.GameMode is CampaignMode campaign)) { return; } + Character targetCharacter = null; + + if (args.Length >= 2) { - if (int.TryParse(args[0], out int money)) - { - campaign.Bank.Give(money); - GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console"); - } - else - { - ThrowError($"\"{args[0]}\" is not a valid numeric value."); - } + targetCharacter = FindMatchingCharacter(args.Skip(1).ToArray()); + } + + if (int.TryParse(args[0], out int money)) + { + Wallet wallet = targetCharacter is null || GameMain.IsSingleplayer ? campaign.Bank : targetCharacter.Wallet; + wallet.Give(money); + GameAnalyticsManager.AddMoneyGainedEvent(money, GameAnalyticsManager.MoneySource.Cheat, "console"); + } + else + { + ThrowError($"\"{args[0]}\" is not a valid numeric value."); } }, isCheat: true, getValidArgs: () => new [] { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Decals/DecalManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Decals/DecalManager.cs index 2b0cd4756..af71aa722 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Decals/DecalManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Decals/DecalManager.cs @@ -1,10 +1,8 @@ -using Microsoft.Xna.Framework; +using Barotrauma.Extensions; +using Microsoft.Xna.Framework; using System; -using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; using System.Xml.Linq; -using Barotrauma.Extensions; namespace Barotrauma { @@ -33,11 +31,11 @@ namespace Barotrauma public static int GrimeSpriteCount { get; private set; } = 0; public static readonly PrefabCollection GrimeSprites = new PrefabCollection( - onAdd: (sprite, b) => GrimeSpriteCount = Math.Max(GrimeSpriteCount, sprite.IndexInFile+1), + onAdd: (sprite, b) => GrimeSpriteCount = Math.Max(GrimeSpriteCount, sprite.IndexInFile + 1), onRemove: (s) => GrimeSpriteCount = GrimeSprites.AllPrefabs .SelectMany(kvp => kvp.Value) - .Where(p => p != s).Select(p => p.IndexInFile+1).MaxOrNull() ?? 0, + .Where(p => p != s).Select(p => p.IndexInFile + 1).MaxOrNull() ?? 0, onSort: null, onAddOverrideFile: null, onRemoveOverrideFile: null); public static void LoadFromFile(DecalsFile configFile) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs index 11ed0fd52..9e3c000b3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs @@ -102,7 +102,7 @@ namespace Barotrauma item.GetComponent() != null || item.GetComponent() != null) { - item.Indestructible = true; + item.InvulnerableToDamage = true; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs index e10894b20..1005729bc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs @@ -210,6 +210,13 @@ namespace Barotrauma }; } + public string GetOwnerLogName() => Owner switch + { + Some { Value: var character } => character.Name, + None _ => "the bank", + _ => throw new ArgumentOutOfRangeException(nameof(Owner)) + }; + partial void SettingsChanged(Option balanceChanged, Option rewardChanged); private static int ClampBalance(int value) => Math.Clamp(value, 0, CampaignMode.MaxMoney); diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index 44a2cdf9b..43e206d8c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -147,22 +147,23 @@ namespace Barotrauma var campaign = SinglePlayerCampaign.Load(subElement); campaign.LoadNewLevel(); GameMode = campaign; + InitOwnedSubs(submarineInfo, ownedSubmarines); break; #endif case "multiplayercampaign": CrewManager = new CrewManager(false); var mpCampaign = MultiPlayerCampaign.LoadNew(subElement); GameMode = mpCampaign; - if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) + if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { - mpCampaign.LoadNewLevel(); + mpCampaign.LoadNewLevel(); + InitOwnedSubs(submarineInfo, ownedSubmarines); //save to ensure the campaign ID in the save file matches the one that got assigned to this campaign instance SaveUtil.SaveGame(saveFile); } break; } } - InitOwnedSubs(submarineInfo); } private void InitOwnedSubs(SubmarineInfo submarineInfo, List? ownedSubmarines = null) @@ -409,14 +410,16 @@ namespace Barotrauma if (GameMain.NetworkMember?.ServerSettings?.LockAllDefaultWires ?? false) { - foreach (Item item in Item.ItemList) + List items = new List(); + items.AddRange(Submarine.MainSubs[0].GetItems(alsoFromConnectedSubs: true)); + if (Submarine.MainSubs[1] != null) { - if (item.Submarine == Submarine.MainSubs[0] || - (Submarine.MainSubs[1] != null && item.Submarine == Submarine.MainSubs[1])) - { - Wire wire = item.GetComponent(); - if (wire != null && !wire.NoAutoLock && wire.Connections.Any(c => c != null)) { wire.Locked = true; } - } + items.AddRange(Submarine.MainSubs[1].GetItems(alsoFromConnectedSubs: true)); + } + foreach (Item item in items) + { + Wire wire = item.GetComponent(); + if (wire != null && !wire.NoAutoLock && wire.Connections.Any(c => c != null)) { wire.Locked = true; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs index 1319254d4..7a624ac27 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs @@ -228,9 +228,12 @@ namespace Barotrauma.Items.Components { if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection)) { - Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos); - rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f); - if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; } + if (!item.CurrentHull.ConnectedGaps.Any(g => g.Open > 0.0f && Submarine.RectContains(g.Rect, hullIntersection))) + { + Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos); + rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f); + if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; } + } } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index 028e59a8e..df622c16c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -251,7 +251,7 @@ namespace Barotrauma return true; } #endif - + if (HiddenInGame) { return false; } if (character != null && character.IsOnPlayerTeam) { return IsPlayerTeamInteractable; @@ -1438,7 +1438,6 @@ namespace Barotrauma public bool HasAccess(Character character) { - if (HiddenInGame) { return false; } if (character.IsBot && IgnoreByAI(character)) { return false; } if (!IsInteractable(character)) { return false; } var itemContainer = GetComponent(); @@ -1834,10 +1833,29 @@ namespace Barotrauma Submarine prevSub = Submarine; var projectile = GetComponent(); - if (projectile?.StickTarget?.UserData is Limb limb && limb.character != null) + if (projectile?.StickTarget != null) { - Submarine = body.Submarine = limb.character.Submarine; - currentHull = limb.character.CurrentHull; + if (projectile?.StickTarget.UserData is Limb limb && limb.character != null) + { + Submarine = body.Submarine = limb.character.Submarine; + currentHull = limb.character.CurrentHull; + } + else if (projectile.StickTarget.UserData is Structure structure) + { + Submarine = body.Submarine = structure.Submarine; + currentHull = Hull.FindHull(WorldPosition, CurrentHull); + } + else if (projectile.StickTarget.UserData is Item targetItem) + { + Submarine = body.Submarine = targetItem.Submarine; + currentHull = targetItem.CurrentHull; + } + else if (projectile.StickTarget.UserData is Submarine) + { + //attached to a sub from the outside -> don't move inside the sub + Submarine = body.Submarine = null; + currentHull = null; + } } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs index 89ee34a92..8ab8ee918 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Explosion.cs @@ -133,6 +133,7 @@ namespace Barotrauma { displayRange *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionRadius); Attack.DamageMultiplier *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionDamage); + Attack.SourceItem ??= sourceItem; } Vector2 cameraPos = GameMain.GameScreen.Cam.Position; @@ -337,11 +338,17 @@ namespace Barotrauma } } + AbilityAttackData attackData = new AbilityAttackData(Attack, c, attacker); + if (attackData.Afflictions != null) + { + modifiedAfflictions.AddRange(attackData.Afflictions); + } + //use a position slightly from the limb's position towards the explosion //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods Vector2 dir = worldPosition - limb.WorldPosition; Vector2 hitPos = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f; - AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker, damageMultiplier: attack.DamageMultiplier); + AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker, damageMultiplier: attack.DamageMultiplier * attackData.DamageMultiplier); damages.Add(limb, attackResult.Damage); if (attack.StatusEffects != null && attack.StatusEffects.Any()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs index e531500bf..f4172ff8f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs @@ -23,6 +23,10 @@ namespace Barotrauma public readonly Vector2 Noise; public readonly Color DirtColor; +#if CLIENT + public Sprite GrimeSprite; +#endif + public float ColorStrength { get; @@ -51,6 +55,9 @@ namespace Barotrauma PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f)); Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X); +#if CLIENT + GrimeSprite = DecalManager.GrimeSprites[$"{nameof(GrimeSprite)}{index % DecalManager.GrimeSpriteCount}"].Sprite; +#endif } public BackgroundSection(Rectangle rect, ushort index, float colorStrength, Color color, ushort rowIndex) @@ -68,6 +75,9 @@ namespace Barotrauma PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f)); DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X); +#if CLIENT + GrimeSprite = DecalManager.GrimeSprites[$"{nameof(GrimeSprite)}{index % DecalManager.GrimeSpriteCount}"].Sprite; +#endif } public bool SetColor(Color color) @@ -274,33 +284,17 @@ namespace Barotrauma get { return Submarine == null ? surface : surface + Submarine.Position.Y; } } - private float dirtiedVolume = 0.0f; - public float WaterVolume { get { return waterVolume; } set { - if (!MathUtils.IsValid(value)) return; + if (!MathUtils.IsValid(value)) { return; } waterVolume = MathHelper.Clamp(value, 0.0f, Volume * MaxCompress); if (waterVolume < Volume) { Pressure = rect.Y - rect.Height + waterVolume / rect.Width; } if (waterVolume > 0.0f) { update = true; - if (BackgroundSections != null) - { - float volumeMultiplier = Math.Clamp(waterVolume / Volume, 0f, 1f); - if (Math.Abs(volumeMultiplier - dirtiedVolume) > 0.075f) - { - RefreshSubmergedSections(new Rectangle(new Point(0, -rect.Height), new Point(rect.Width, (int)(rect.Height * volumeMultiplier)))); - dirtiedVolume = volumeMultiplier; - } - } - } - else - { - submergedSections.Clear(); - dirtiedVolume = 0.0f; } } } @@ -391,8 +385,6 @@ namespace Barotrauma private readonly HashSet pendingSectionUpdates = new HashSet(); - private readonly List submergedSections = new List(); - public int xBackgroundMax, yBackgroundMax; public bool SupportsPaintedColors @@ -664,7 +656,6 @@ namespace Barotrauma } BackgroundSections?.Clear(); - submergedSections?.Clear(); List fireSourcesToRemove = new List(FireSources); foreach (FireSource fireSource in fireSourcesToRemove) @@ -973,12 +964,6 @@ namespace Barotrauma } } - //0.016 increase every ~2000 frames = reaches full dirtiness in ~35 minutes - if (submergedSections.Count > 0 && Submarine != null && Submarine.Info.Type == SubmarineType.Player && Rand.Int(2000) == 1) - { - DirtySections(submergedSections, deltaTime); - } - if (waterVolume < Volume) { LethalPressure -= 10.0f * deltaTime; @@ -1451,17 +1436,6 @@ namespace Barotrauma } } - public void RefreshSubmergedSections(Rectangle waterArea) - { - if (BackgroundSections == null) { return; } - - submergedSections.Clear(); - foreach (var section in GetBackgroundSectionsViaContaining(waterArea)) - { - submergedSections.Add(section); - } - } - public bool DoesSectionMatch(int index, int row) { return index >= 0 && row >= 0 && BackgroundSections.Count > index && BackgroundSections[index] != null && BackgroundSections[index].RowIndex == row; @@ -1528,15 +1502,6 @@ namespace Barotrauma } } - public void DirtySections(List sections, float dirtyVal) - { - if (sections == null) { return; } - for (int i = 0; i < sections.Count; i++) - { - IncreaseSectionColorOrStrength(sections[i], sections[i].DirtColor, dirtyVal, false, false); - } - } - public void CleanSection(BackgroundSection section, float cleanVal, bool updateRequired) { bool decalsCleaned = false; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerationParams.cs index 89c509288..501c9d163 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerationParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Ruins/RuinGenerationParams.cs @@ -37,10 +37,14 @@ namespace Barotrauma.RuinGeneration Indent = true, NewLineOnAttributes = true }; - + + IEnumerable packages = ContentPackageManager.LocalPackages; +#if DEBUG + packages = packages.Union(ContentPackageManager.VanillaCorePackage.ToEnumerable()); +#endif foreach (RuinGenerationParams generationParams in RuinParams) { - foreach (RuinConfigFile configFile in ContentPackageManager.AllPackages.SelectMany(p => p.GetFiles())) + foreach (RuinConfigFile configFile in packages.SelectMany(p => p.GetFiles())) { if (configFile.Path != generationParams.ContentFile.Path) { continue; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs index 7a7fa2598..258e7262f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/MapEntityPrefab.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Reflection; -using System.Xml.Linq; using Barotrauma.Extensions; namespace Barotrauma @@ -13,16 +11,20 @@ namespace Barotrauma enum MapEntityCategory { Structure = 1, - Decorative = 2, - Machine = 4, - Equipment = 8, - Electrical = 16, - Material = 32, - Misc = 64, - Alien = 128, - Wrecked = 256, - ItemAssembly = 512, - Legacy = 1024 + Decorative = 2, + Machine = 4, + Medical = 8, + Weapon = 16, + Diving = 32, + Equipment = 64, + Fuel = 128, + Electrical = 256, + Material = 1024, + Alien = 2048, + Wrecked = 4096, + ItemAssembly = 8192, + Legacy = 16384, + Misc = 32768 } abstract partial class MapEntityPrefab : PrefabWithUintIdentifier diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs index 594adb116..295b92751 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs @@ -1540,8 +1540,10 @@ namespace Barotrauma List killedCharacters = new List(); List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)> selectedCharacters = new List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)>(); - foreach (HumanPrefab humanPrefab in outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.ServerAndClient)) + var humanPrefabs = outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.ServerAndClient); + foreach (HumanPrefab humanPrefab in humanPrefabs) { + if (humanPrefab is null) { continue; } var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient); if (location != null && location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier())) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/StructurePrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/StructurePrefab.cs index 26c2f92e0..0a1356efd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/StructurePrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/StructurePrefab.cs @@ -153,6 +153,7 @@ namespace Barotrauma { Name = TextManager.GetWithVariable("wreckeditemformat", "[name]", Name); } + Name = Name.Fallback(OriginalName); var tags = new HashSet(); string joinedTags = element.GetAttributeString("tags", ""); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs index b7edffbbb..84f6527c9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs @@ -139,7 +139,7 @@ namespace Barotrauma.Networking } } - public bool HasPermissions = false; + public bool HasPermissions => Permissions != ClientPermissions.None; public VoipQueue VoipQueue { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerLog.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerLog.cs index a86284f23..fcc0bed07 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerLog.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerLog.cs @@ -38,6 +38,7 @@ namespace Barotrauma.Networking Wiring, ServerMessage, ConsoleUsage, + Money, Karma, Talent, Error, @@ -53,9 +54,10 @@ namespace Barotrauma.Networking { MessageType.Wiring, new Color(255, 157, 85) }, { MessageType.ServerMessage, new Color(157, 225, 160) }, { MessageType.ConsoleUsage, new Color(0, 162, 232) }, + { MessageType.Money, Color.Green }, { MessageType.Karma, new Color(75, 88, 255) }, { MessageType.Talent, new Color(125, 125, 255) }, - { MessageType.Error, Color.Red }, + { MessageType.Error, Color.Red } }; private readonly Dictionary messageTypeName = new Dictionary @@ -68,6 +70,7 @@ namespace Barotrauma.Networking { MessageType.Wiring, "Wiring" }, { MessageType.ServerMessage, "ServerMessage" }, { MessageType.ConsoleUsage, "ConsoleUsage" }, + { MessageType.Money, "Money" }, { MessageType.Karma, "Karma" }, { MessageType.Talent, "Talent" }, { MessageType.Error, "Error" } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty.cs b/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty.cs index 765a62374..d6f2ec544 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Serialization/SerializableProperty.cs @@ -1007,6 +1007,19 @@ namespace Barotrauma } } } + else if (attributeName == "move") + { + Vector2 moveAmount = subElement.GetAttributeVector2("move", Vector2.Zero); + if (entity is Structure structure) + { + structure.Move(moveAmount); + } + else if (entity is Item item) + { + item.Move(moveAmount); + } + continue; + } if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property)) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs index 551b48fdc..7292d5bf0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs @@ -148,6 +148,8 @@ namespace Barotrauma public struct GraphicsSettings { + public static readonly Point MinSupportedResolution = new Point(1024, 540); + public static GraphicsSettings GetDefault() { GraphicsSettings gfxSettings = new GraphicsSettings diff --git a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs index f7445abed..45aa60f26 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs @@ -167,16 +167,9 @@ namespace Barotrauma var type = Type; if (type == ConditionType.Uncertain) { - if (AfflictionPrefab.Prefabs.ContainsKey(AttributeName)) - { - type = ConditionType.Affliction; - } - else - { - type = (target?.SerializableProperties?.ContainsKey(AttributeName) ?? false) - ? ConditionType.PropertyValue - : ConditionType.HasSpecifierTag; - } + type = AfflictionPrefab.Prefabs.ContainsKey(AttributeName) + ? ConditionType.Affliction + : ConditionType.PropertyValue; } if (checkContained) @@ -250,6 +243,14 @@ namespace Barotrauma } } return Operator == OperatorType.Equals ? matches >= SplitAttributeValue.Length : matches <= 0; + case ConditionType.HasSpecifierTag: + { + if (target == null) { return Operator == OperatorType.NotEquals; } + if (!(target is Character { Info: { } characterInfo })) { return false; } + + return (Operator == OperatorType.Equals) == + SplitAttributeValue.All(v => characterInfo.Head.Preset.TagSet.Contains(v)); + } case ConditionType.SpeciesName: { if (target == null) { return Operator == OperatorType.NotEquals; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs index 8f843ba7c..f313f89fc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs @@ -309,6 +309,11 @@ namespace Barotrauma.Steam XDocument fileListSrc = XMLExtensions.TryLoadXml(Path.Combine(itemDirectory, ContentPackage.FileListFileName)); string modName = fileListSrc.Root.GetAttributeString("name", item.Title).Trim(); + string[] modPathSplit = fileListSrc.Root.GetAttributeString("path", "") + .CleanUpPathCrossPlatform(correctFilenameCase: false).Split("/"); + string? modPathDirName = modPathSplit.Length > 1 && modPathSplit[0] == "Mods" + ? modPathSplit[1] + : null; string modVersion = fileListSrc.Root.GetAttributeString("modversion", ContentPackage.DefaultModVersion); Version gameVersion = fileListSrc.Root.GetAttributeVersion("gameversion", GameMain.Version); bool isCorePackage = fileListSrc.Root.GetAttributeBool("corepackage", false); @@ -316,7 +321,7 @@ namespace Barotrauma.Steam using (var copyIndicator = new CopyIndicator(copyIndicatorPath)) { - await CopyDirectory(itemDirectory, modName, itemDirectory, installDir); + await CopyDirectory(itemDirectory, modPathDirName ?? modName, itemDirectory, installDir); string fileListDestPath = Path.Combine(installDir, ContentPackage.FileListFileName); XDocument fileListDest = XMLExtensions.TryLoadXml(fileListDestPath); @@ -330,9 +335,9 @@ namespace Barotrauma.Steam new XAttribute("modversion", modVersion), new XAttribute("gameversion", gameVersion), new XAttribute("installtime", ToolBox.Epoch.FromDateTime(updateTime))); - if (modName.ToIdentifier() != itemTitle) + if ((modPathDirName ?? modName).ToIdentifier() != itemTitle) { - root.Add(new XAttribute("altnames", modName)); + root.Add(new XAttribute("altnames", modPathDirName ?? modName)); } if (!expectedHash.IsNullOrEmpty()) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/RichString.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/RichString.cs index ff86da1d4..67cb2fbf0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/RichString.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/RichString.cs @@ -83,7 +83,7 @@ namespace Barotrauma if (Debugger.IsAttached) { Debugger.Break(); } } #endif - return Plain(lStr); + return Plain(lStr ?? string.Empty); } public static implicit operator RichString(string str) => (LocalizedString)str; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs index b4829e7ba..e36603dd7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/TextPack.cs @@ -149,22 +149,34 @@ namespace Barotrauma { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < Texts.Count; i++) + XDocument doc = XMLExtensions.TryLoadXml(ContentFile.Path); + if (doc == null) { return; } + + List<(string key, string value)> texts = new List<(string key, string value)>(); + + foreach (var element in doc.Root.Elements()) { - Identifier key = Texts.Keys.ElementAt(i); - Texts.TryGetValue(key, out ImmutableArray infoList); + string text = element.ElementInnerText() + .Replace("&", "&") + .Replace("<", "<") + .Replace(">", ">") + .Replace(""", "\"") + .Replace("'", "'"); + + texts.Add((element.Name.ToString(), text)); + } + + foreach ((string key, string value) in texts) + { + sb.Append(key); // ID + sb.Append('*'); + sb.Append(value); // Original + sb.Append('*'); + // Translated + sb.Append('*'); + // Comments + sb.AppendLine(); - for (int j = 0; j < infoList.Length; j++) - { - sb.Append(key); // ID - sb.Append('*'); - sb.Append(infoList[j]); // Original - sb.Append('*'); - // Translated - sb.Append('*'); - // Comments - sb.AppendLine(); - } } Barotrauma.IO.File.WriteAllText($"csv_{Language.ToString().ToLower()}_{index}.csv", sb.ToString()); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/Result.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/Result.cs index 71de5e5a3..b63e7a2bc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/Result.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/Result.cs @@ -11,8 +11,8 @@ namespace Barotrauma public static Success Success(T value) => new Success(value); - public static Failure Failure(TError error) - => new Failure(error); + public static Failure Failure(TError error, string? stackTrace) + => new Failure(error, stackTrace); } public sealed class Success : Result @@ -33,11 +33,15 @@ namespace Barotrauma where TError: notnull { public readonly TError Error; + + public readonly string? StackTrace; + public override bool IsSuccess => false; - public Failure(TError error) + public Failure(TError error, string? stackTrace) { Error = error; + StackTrace = stackTrace; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs index 40f135e26..c4acca250 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Barotrauma.Networking; namespace Barotrauma.IO { @@ -23,9 +24,15 @@ namespace Barotrauma.IO public static bool CanWrite(string path, bool isDirectory) { - path = System.IO.Path.GetFullPath(path).CleanUpPath(); - string localModsDir = System.IO.Path.GetFullPath(ContentPackage.LocalModsDir).CleanUpPath(); - string workshopModsDir = System.IO.Path.GetFullPath(ContentPackage.WorkshopModsDir).CleanUpPath(); + string getFullPath(string p) + => System.IO.Path.GetFullPath(p).CleanUpPath(); + + path = getFullPath(path); + string localModsDir = getFullPath(ContentPackage.LocalModsDir); + string workshopModsDir = getFullPath(ContentPackage.WorkshopModsDir); +#if CLIENT + string tempDownloadDir = getFullPath(ModReceiver.DownloadFolder); +#endif if (!isDirectory) { @@ -34,8 +41,15 @@ namespace Barotrauma.IO { return false; } - if (!path.StartsWith(workshopModsDir, StringComparison.OrdinalIgnoreCase) - && !path.StartsWith(localModsDir, StringComparison.OrdinalIgnoreCase) + + bool pathStartsWith(string prefix) + => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); + + if (!pathStartsWith(workshopModsDir) + && !pathStartsWith(localModsDir) +#if CLIENT + && !pathStartsWith(tempDownloadDir) +#endif && (extension == ".dll" || extension == ".exe" || extension == ".json")) { return false; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs index e0b919969..b55e03a9f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs @@ -13,7 +13,7 @@ using System.Text.RegularExpressions; namespace Barotrauma { - partial class SaveUtil + static class SaveUtil { private static readonly string LegacySaveFolder = Path.Combine("Data", "Saves"); private static readonly string LegacyMultiplayerSaveFolder = Path.Combine(LegacySaveFolder, "Multiplayer"); diff --git a/Barotrauma/BarotraumaShared/Submarines/PowerTestSub.sub b/Barotrauma/BarotraumaShared/Submarines/PowerTestSub.sub deleted file mode 100644 index ea439c4cb..000000000 Binary files a/Barotrauma/BarotraumaShared/Submarines/PowerTestSub.sub and /dev/null differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index e2b509a75..a0696f27c 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,58 @@ +--------------------------------------------------------------------------------------------------------- +v0.17.7.0 +--------------------------------------------------------------------------------------------------------- + +Changes: +- Water no longer dirties up walls. +- New store (and sub editor) categories: weapon, medical, diving, and fuel. Reorganized the items into categories. +- Disabled store category buttons for categories that contain no items. + +Changes (unstable only): +- Content package errors are logged in the debug console when enabling a mod that has errors, + listed in tooltips in the Mods tab. +- Updated sprites for the wallet related features. +- Personal wallet balance now shows in the bottom right character portrait. +- Latcher: new sounds and minor tweaks. + +Fixes (unstable only): +- Fixed crashing when pressing the "retry" button in the pause menu when the outpost store interface is open. +- Fixed crashing when opening the Mods tab when not connected to Steam. +- Fixed structures not using the name defined in xml when the name can't be found in loca files. +- Fixed inability to start additional votes when another vote is running. +- Fixed buying from outpost vendors requiring sell permissions. +- Fixed clients with the ManagePermissions being able to edit the host's permissions (didn't actually do anything, but the menu still showed up). +- Fixed inability to repair beacon station devices after the beacon is activated. +- Fixed tiny plus/minus buttons in spawnpoint editing menu. +- Fixed vomiting sounds not playing. +- Fixed some mods failing to extract after being downloaded from the server. +- Fixed crashing when deleting a Workshop item that has music-related errors. +- Fixed submarine editor listing recently disabled subs in the Load menu. +- Fixed certain old mods not having their paths corrected properly when installed through the Workshop or transferred from the Mods folder. +- Optimized file transfers. +- Disabled unsupported resolutions in the settings menu. +- Fixed crashing in event editor when loading prefabs or projects. +- Fixed ping and wallet columns switching places in tab menu when a character dies. +- Fixed "Lots!" text appearing too eagerly when playing on lower resolutions. +- Fixed monsters using fast swim parameters when they swim slowly. +- Fixed human head sometimes shaking while aiming. + +Fixes: +- Fixed occasional performance dips when spineling spikes get stuck to the sub's exterior walls. +- Fixed item assemblies getting misaligned with the grid after saving. +- Fixed "Shell A 18" not aligning with the other shell pieces. +- Fixed "Deep Sea Slayer" talent not affecting explosive harpoons. +- Fixed welding tool's, plasma cutter's and watering can's particle effects getting "clamped" to the edges of the hull they're inside. +- Fixed permission icon in the client list not updating mid-round. +- Fixed "lock default wires" server setting not affecting docked subs. +- Fixed Dugong's small pumps working without power. +- Fixed buttons in structure editing menu using a different style than other types of entities in the sub editor. +- Fixed items flagged as "HiddenInGame" being considered interactable and therefore e.g. valid repair targets. +- Fixed some (hopefully for good) issues bots still had with ladders. +- Fixed outpost guards not arresting the offender when it's very far from them. +- Fixed bots sometimes failing to navigate back to the sub (when they are on the other side of the sub than where the hatch is). + +Modding: +- Level editor no longer attempts to save the vanilla content. + --------------------------------------------------------------------------------------------------------- v0.17.6.0 --------------------------------------------------------------------------------------------------------- @@ -105,6 +160,8 @@ Fixes (unstable only): - Fixed flashlights emitting sparks. - Flipped the confirm and reset buttons in wallet UI. - Added a new column to multiplayer tab menu that shows the amount of money the player has in their wallet. +- Added psychosis and hallucination reduction to pipe tobacco, cigars and ethanol, rather than just giving psychosis resistance. +- Reduced base price of tobacco products (pipe tobacco and cigars) Modding: - Option to make missions force a ruin in the level if there isn't one. diff --git a/Libraries/Facepunch.Steamworks/SteamUgc.cs b/Libraries/Facepunch.Steamworks/SteamUgc.cs index 691bf0688..059f3f071 100644 --- a/Libraries/Facepunch.Steamworks/SteamUgc.cs +++ b/Libraries/Facepunch.Steamworks/SteamUgc.cs @@ -188,6 +188,14 @@ namespace Steamworks public static Action GlobalOnItemInstalled; public static uint NumSubscribedItems { get { return Internal.GetNumSubscribedItems(); } } + + public static PublishedFileId[] GetSubscribedItems() + { + uint numSubscribed = NumSubscribedItems; + PublishedFileId[] ids = new PublishedFileId[numSubscribed]; + Internal.GetSubscribedItems(ids, numSubscribed); + return ids; + } } }