From 95764d1fa88748cb4d1d59dcfa13dca8b267d4e6 Mon Sep 17 00:00:00 2001 From: Markus Isberg <3e849f2e5c@pm.me> Date: Mon, 4 Apr 2022 16:46:08 +0900 Subject: [PATCH] Unstable 0.17.6.0 --- .../Transition/UgcTransition.cs | 166 ++++++++--- .../ClientSource/DebugConsole.cs | 6 +- .../ClientSource/GUI/ChatBox.cs | 11 +- .../ClientSource/GUI/FileSelection.cs | 4 +- .../BarotraumaClient/ClientSource/GUI/GUI.cs | 7 +- .../ClientSource/GUI/GUIContextMenu.cs | 13 +- .../ClientSource/GUI/GUITextBox.cs | 9 +- .../ClientSource/GUI/Store.cs | 56 ++-- .../ClientSource/GUI/SubmarineSelection.cs | 17 +- .../ClientSource/GUI/VotingInterface.cs | 4 +- .../BarotraumaClient/ClientSource/GameMain.cs | 4 +- .../ClientSource/GameSession/CrewManager.cs | 8 +- .../GameModes/MultiPlayerCampaign.cs | 18 ++ .../Items/Components/Machines/Fabricator.cs | 27 +- .../Items/Components/StatusHUD.cs | 2 +- .../ClientSource/Items/Item.cs | 45 +-- .../ClientSource/Map/Structure.cs | 2 +- .../ClientSource/Networking/Client.cs | 19 +- .../ClientSource/Networking/GameClient.cs | 73 +---- .../ClientSource/Networking/Voting.cs | 7 +- .../ClientSource/Particles/ParticlePrefab.cs | 5 +- .../ClientSource/Screens/LevelEditorScreen.cs | 6 +- .../ClientSource/Screens/MainMenuScreen.cs | 1 - .../ClientSource/Screens/ModDownloadScreen.cs | 31 +- .../ClientSource/Screens/NetLobbyScreen.cs | 64 ++-- .../ClientSource/Screens/SubEditorScreen.cs | 142 +++------ .../ClientSource/Sounds/SoundPlayer.cs | 31 ++ .../ClientSource/Sounds/SoundPrefab.cs | 1 + .../ClientSource/Steam/BulkDownloader.cs | 125 ++++++++ .../Immutable/ImmutableWorkshopMenu.cs | 22 +- .../Mutable/MutableWorkshopMenu.cs | 278 +++++++++++++----- .../ClientSource/Steam/WorkshopMenu/UiUtil.cs | 24 ++ .../Steam/WorkshopMenu/WorkshopMenu.cs | 7 +- .../BarotraumaClient/LinuxClient.csproj | 2 +- Barotrauma/BarotraumaClient/MacClient.csproj | 2 +- .../BarotraumaClient/WindowsClient.csproj | 2 +- .../BarotraumaServer/LinuxServer.csproj | 2 +- Barotrauma/BarotraumaServer/MacServer.csproj | 2 +- .../ServerSource/DebugConsole.cs | 7 +- .../ServerSource/GameSession/CargoManager.cs | 4 +- .../GameModes/MultiPlayerCampaign.cs | 37 ++- .../Items/Components/Machines/Fabricator.cs | 12 +- .../ServerSource/Networking/BanList.cs | 2 +- .../Networking/FileTransfer/FileSender.cs | 8 +- .../Networking/FileTransfer/ModSender.cs | 2 +- .../ServerSource/Networking/GameServer.cs | 66 ++--- .../BarotraumaServer/WindowsServer.csproj | 2 +- .../Characters/AI/EnemyAIController.cs | 2 +- .../Characters/AI/IndoorsSteeringManager.cs | 27 +- .../AI/Objectives/AIObjectiveManager.cs | 2 +- .../Characters/Animation/AnimController.cs | 2 + .../Animation/FishAnimController.cs | 4 +- .../Animation/HumanoidAnimController.cs | 102 +++---- .../Characters/Animation/Ragdoll.cs | 61 +++- .../SharedSource/Characters/Attack.cs | 14 +- .../SharedSource/Characters/Character.cs | 50 +++- .../Characters/Health/CharacterHealth.cs | 12 +- .../Params/Ragdoll/RagdollParams.cs | 11 +- .../ContentFile/ItemAssemblyFile.cs | 1 + .../ContentManagement/ContentFile/TextFile.cs | 1 - .../ContentPackageManager.cs | 13 + .../SharedSource/Events/EventSet.cs | 13 +- .../Events/Missions/BeaconMission.cs | 2 +- .../SharedSource/GameSession/GameSession.cs | 32 +- .../Items/Components/Machines/Fabricator.cs | 16 +- .../SharedSource/Items/Item.cs | 4 +- .../SharedSource/Items/ItemPrefab.cs | 6 + .../SharedSource/Map/ItemAssemblyPrefab.cs | 1 - .../Levels/LevelObjects/LevelObjectManager.cs | 2 +- .../SharedSource/Map/Map/Location.cs | 6 +- .../SharedSource/Map/PriceInfo.cs | 28 +- .../SharedSource/Map/WayPoint.cs | 45 ++- .../SharedSource/Networking/Client.cs | 19 ++ .../SharedSource/Screens/GameScreen.cs | 7 + .../SharedSource/Steam/Workshop.cs | 6 +- .../SharedSource/Text/TextManager.cs | 14 + Barotrauma/BarotraumaShared/changelog.txt | 62 ++++ Libraries/Facepunch.Steamworks/SteamUgc.cs | 18 +- 78 files changed, 1265 insertions(+), 703 deletions(-) create mode 100644 Barotrauma/BarotraumaClient/ClientSource/Steam/BulkDownloader.cs diff --git a/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs b/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs index 026b93043..537ca3df8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; using Barotrauma.Extensions; using Barotrauma.Steam; using Microsoft.Xna.Framework; @@ -26,9 +27,9 @@ namespace Barotrauma.Transition { TaskPool.Add("UgcTransition.Prepare", DetermineItemsToTransition(), t => { - if (!t.TryGetResult(out (OldSubs, OldMods) pair)) { return; } - var (subs, mods) = pair; - if (!subs.FilePaths.Any() && !mods.Mods.Any()) { return; } + if (!t.TryGetResult(out (OldSubs, OldItemAssemblies, OldMods) result)) { return; } + var (subs, itemAssemblies, mods) = result; + if (!subs.FilePaths.Any() && !itemAssemblies.FilePaths.Any() && !mods.Mods.Any()) { return; } var msgBox = new GUIMessageBox(TextManager.Get("Ugc.TransferTitle"), "", relativeSize: (0.5f, 0.8f), buttons: new LocalizedString[] { TextManager.Get("Ugc.TransferButton") }); @@ -63,19 +64,45 @@ namespace Barotrauma.Transition }; pathTickboxMap.Add(dir, tickbox); } - - addHeader(TextManager.Get("WorkshopLabelSubmarines")); - foreach (var sub in subs.FilePaths) + + bool firstHeader = true; + + void addSpacer() { - var subName = Path.GetFileNameWithoutExtension(sub); - addTickbox(sub, subName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(subName))); + if (firstHeader) { firstHeader = false; return; } + addHeader(""); } - addHeader(""); - addHeader(TextManager.Get("SubscribedMods")); - foreach (var mod in mods.Mods) + if (subs.FilePaths.Any()) { - addTickbox(mod.Dir, mod.Name, ticked: !ContentPackageManager.LocalPackages.Any(p => p.SteamWorkshopId != 0 && p.SteamWorkshopId == mod.Item?.Id)); + addSpacer(); + addHeader(TextManager.Get("WorkshopLabelSubmarines")); + foreach (var sub in subs.FilePaths) + { + var subName = Path.GetFileNameWithoutExtension(sub); + addTickbox(sub, subName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(subName))); + } + } + + if (itemAssemblies.FilePaths.Any()) + { + addSpacer(); + addHeader(TextManager.Get("ItemAssemblies")); + foreach (var itemAssembly in itemAssemblies.FilePaths) + { + var assemblyName = Path.GetFileNameWithoutExtension(itemAssembly); + addTickbox(itemAssembly, assemblyName, ticked: !ContentPackageManager.LocalPackages.Any(p => p.NameMatches(assemblyName))); + } + } + + if (mods.Mods.Any()) + { + addSpacer(); + addHeader(TextManager.Get("SubscribedMods")); + foreach (var mod in mods.Mods) + { + addTickbox(mod.Dir, mod.Name, ticked: !ContentPackageManager.LocalPackages.Any(p => p.SteamWorkshopId != 0 && p.SteamWorkshopId == mod.Item?.Id)); + } } GUIMessageBox? subMsgBox = null; @@ -119,6 +146,16 @@ namespace Barotrauma.Transition } } + private struct OldItemAssemblies + { + public readonly IReadOnlyList FilePaths; + + public OldItemAssemblies(IReadOnlyList filePaths) + { + FilePaths = filePaths; + } + } + private struct OldMods { public readonly IReadOnlyList<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> Mods; @@ -131,15 +168,24 @@ namespace Barotrauma.Transition private const string oldSubsPath = "Submarines"; private const string oldModsPath = "Mods"; + private const string oldItemAssembliesPath = "ItemAssemblies"; - private static async Task<(OldSubs Subs, OldMods Mods)> DetermineItemsToTransition() + private static async Task<(OldSubs Subs, OldItemAssemblies ItemAssemblies, OldMods Mods)> DetermineItemsToTransition() { string[] subs = Array.Empty(); + string[] itemAssemblies = Array.Empty(); List<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> mods = new List<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)>(); if (FolderShouldBeTransitioned(oldModsPath)) { - subs = Directory.GetFiles(oldSubsPath, "*.sub", SearchOption.TopDirectoryOnly); + string[] getFiles(string path, string pattern) + => Directory.Exists(path) + ? Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly) + : Array.Empty(); + + subs = getFiles(oldSubsPath, "*.sub"); + itemAssemblies = getFiles(oldItemAssembliesPath, "*.xml"); + string[] allOldMods = Directory.GetDirectories(oldModsPath, "*", SearchOption.TopDirectoryOnly); var publishedItems = await SteamManager.Workshop.GetPublishedItems(); @@ -160,9 +206,9 @@ namespace Barotrauma.Transition } } - while (!(Screen.Selected is MainMenuScreen)) { await Task.Delay(500); } + while (!(Screen.Selected is MainMenuScreen)) { await Task.Delay(50); } - return (new OldSubs(subs), new OldMods(mods)); + return (new OldSubs(subs), new OldItemAssemblies(itemAssemblies), new OldMods(mods)); } private static bool FolderShouldBeTransitioned(string folderName) @@ -173,45 +219,71 @@ namespace Barotrauma.Transition private static async Task TransferMods(Dictionary pathTickboxMap) { - //WriteReadme(oldSubsPath); //can't do this because the submarine discovery code is borked + //WriteReadme(oldSubsPath); //can't do this because the old submarine discovery code is borked WriteReadme(oldModsPath); - foreach (var (path, tickbox) in pathTickboxMap) - { - if (!tickbox.Selected) { continue; } - string dirName = Path.GetFileNameWithoutExtension(path); - string destPath = Path.Combine(ContentPackage.LocalModsDir, dirName); - - //find unique path to save in - for (int i = 0;;i++) - { - if (!Directory.Exists(destPath)) { break; } - destPath = Path.Combine(ContentPackage.LocalModsDir, $"{dirName}.{i}"); - } - - if (path.StartsWith(oldSubsPath, StringComparison.OrdinalIgnoreCase)) - { - //copying a sub: manually create filelist.xml - ModProject modProject = new ModProject - { - Name = dirName, - ModVersion = ContentPackage.DefaultModVersion - }; - modProject.AddFile(ModProject.File.FromPath(Path.Combine(ContentPath.ModDirStr, $"{dirName}.sub"))); + await Task.WhenAll(pathTickboxMap.Select(TransferMod)); + } - Directory.CreateDirectory(destPath); - File.Copy(path, Path.Combine(destPath, $"{dirName}.sub")); - modProject.Save(Path.Combine(destPath, ContentPackage.FileListFileName)); - - await Task.Yield(); + private static Task TransferMod(KeyValuePair kvp) + => TransferMod(kvp.Key, kvp.Value); + + private static async Task TransferMod(string path, GUITickBox tickbox) + { + if (!tickbox.Selected) { return; } + string dirName = Path.GetFileNameWithoutExtension(path); + string destPath = Path.Combine(ContentPackage.LocalModsDir, dirName); + + //find unique path to save in + for (int i = 0;;i++) + { + if (!Directory.Exists(destPath)) { break; } + destPath = Path.Combine(ContentPackage.LocalModsDir, $"{dirName}.{i}"); + } + + bool isSub = path.StartsWith(oldSubsPath, StringComparison.OrdinalIgnoreCase); + bool isItemAssembly = path.StartsWith(oldItemAssembliesPath, StringComparison.OrdinalIgnoreCase); + if (isSub || isItemAssembly) + { + //copying a sub or item assembly: manually create filelist.xml + ModProject modProject = new ModProject + { + Name = dirName, + ModVersion = ContentPackage.DefaultModVersion + }; + + Type fileType; + if (isSub) + { + fileType = typeof(SubmarineFile); + XDocument? doc = SubmarineInfo.OpenFile(path, out _); + if (doc?.Root != null) + { + SubmarineType subType = doc.Root.GetAttributeEnum("type", SubmarineType.Player); + fileType = SubEditorScreen.DetermineSubFileType(subType); + } } else { - //copying a mod: we have a neat method for that! - await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath); + fileType = typeof(ItemAssemblyFile); } + + modProject.AddFile(ModProject.File.FromPath( + Path.Combine(ContentPath.ModDirStr, $"{dirName}.{(isSub ? "sub" : "xml")}"), + fileType)); + + Directory.CreateDirectory(destPath); + File.Copy(path, Path.Combine(destPath, $"{dirName}.{(isSub ? "sub" : "xml")}")); + modProject.Save(Path.Combine(destPath, ContentPackage.FileListFileName)); + + await Task.Yield(); + } + else + { + //copying a mod: we have a neat method for that! + await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath); } } - + private static void WriteReadme(string folderName) { if (!Directory.Exists(folderName)) { return; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 57d4bc38f..c9c538081 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -3310,11 +3310,7 @@ namespace Barotrauma } depth += " "; - - if (newPrice > 0) - { - newPrices.TryAdd(materialPrefab, newPrice); - } + newPrices.TryAdd(materialPrefab, newPrice); int componentCost = 0; int newComponentCost = 0; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs index 9c175f289..c1ee89fb8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs @@ -25,12 +25,14 @@ namespace Barotrauma get { return _toggleOpen; } set { - _toggleOpen = value; - if (value) hideableElements.Visible = true; + _toggleOpen = PreferChatBoxOpen = value; + if (value) { hideableElements.Visible = true; } } } private float openState; + public static bool PreferChatBoxOpen = true; + public bool CloseAfterMessageSent; private float prevUIScale; @@ -99,6 +101,7 @@ namespace Barotrauma var channelSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), channelSettingsFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true, + CanBeFocused = true, RelativeSpacing = 0.01f }; @@ -119,7 +122,7 @@ namespace Barotrauma Color = new Color(51, 59, 46), SpriteEffects = Microsoft.Xna.Framework.Graphics.SpriteEffects.FlipHorizontally }; - arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.PressedColor = arrowIcon.Color; + arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.SelectedColor = arrowIcon.Color; channelText = new GUITextBox(new RectTransform(new Vector2(0.25f, 0.8f), channelSettingsContent.RectTransform), style: "DigitalFrameLight", textAlignment: Alignment.Center, font: GUIStyle.DigitalFont) { @@ -265,7 +268,7 @@ namespace Barotrauma }; showNewMessagesButton.Visible = false; - ToggleOpen = GameSettings.CurrentConfig.ChatOpen; + ToggleOpen = PreferChatBoxOpen = GameSettings.CurrentConfig.ChatOpen; } public bool TypingChatMessage(GUITextBox textBox, string text) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs index 2f54a3d4d..71023ab04 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs @@ -415,9 +415,9 @@ namespace Barotrauma if (dir.EndsWith("/")) { dir = dir.Substring(0, dir.Length - 1); } int index = dir.LastIndexOf("/"); if (index < 0) { return false; } - CurrentDirectory = CurrentDirectory.Substring(0, index+1); + CurrentDirectory = CurrentDirectory.Substring(0, index + 1); - return false; + return true; } public static void AddToGUIUpdateList() diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs index 88b835ede..3a6ecfd23 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs @@ -343,7 +343,7 @@ namespace Barotrauma foreach (string childKey in GameMain.PerformanceCounter.GetSavedPartialIdentifiers(key)) { elapsedMillisecs = GameMain.PerformanceCounter.GetPartialAverageElapsedMillisecs(key, childKey); - DrawString(spriteBatch, new Vector2(315, y), + DrawString(spriteBatch, new Vector2(x + 15, y), childKey + ": " + elapsedMillisecs.ToString("0.00"), Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont); y += 15; @@ -1422,8 +1422,9 @@ namespace Barotrauma public static void DrawString(SpriteBatch sb, Vector2 pos, string text, Color color, Color? backgroundColor = null, int backgroundPadding = 0, GUIFont font = null, ForceUpperCase forceUpperCase = ForceUpperCase.Inherit) { - if (font == null) font = GUIStyle.Font; - if (backgroundColor != null) + if (color.A == 0) { return; } + if (font == null) { font = GUIStyle.Font; } + if (backgroundColor != null && backgroundColor.Value.A > 0) { Vector2 textSize = font.MeasureString(text); DrawRectangle(sb, pos - Vector2.One * backgroundPadding, textSize + Vector2.One * 2.0f * backgroundPadding, (Color)backgroundColor, true); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs index 2de088d11..eeebb32c4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIContextMenu.cs @@ -95,14 +95,21 @@ namespace Barotrauma // Construct the GUI elements //---------------------------------------------------------------------------------- - GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center)); + GUILayoutGroup background = new GUILayoutGroup(new RectTransform(Vector2.One, RectTransform, Anchor.Center)) + { + Stretch = true + }; + Point listSize = estimatedSize; if (hasHeader) { - HeaderLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.2f), background.RectTransform), header, font: headerFont) { Padding = headerPadding }; + Point sz = Point.Zero; + InflateSize(ref sz, header, headerFont); + listSize.Y -= sz.Y; + HeaderLabel = new GUITextBlock(new RectTransform(sz, background.RectTransform), header, font: headerFont) { Padding = headerPadding }; } - GUIListBox optionList = new GUIListBox(new RectTransform(new Vector2(1f, hasHeader ? 0.8f : 1f), background.RectTransform), style: null) + GUIListBox optionList = new GUIListBox(new RectTransform(listSize, background.RectTransform), style: null) { AutoHideScrollBar = false, ScrollBarVisible = false, diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs index 4635c2150..b4edb64ce 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs @@ -316,6 +316,7 @@ namespace Barotrauma } if (Text == text) { return false; } textBlock.Text = text; + ClearSelection(); if (Text == null) textBlock.Text = ""; if (Text != "" && !Wrap) { @@ -808,10 +809,10 @@ namespace Barotrauma { if (selectedText.Length == 0) { return; } - selectionStartIndex = Math.Max(0, Math.Min(selectionEndIndex, Math.Min(selectionStartIndex, Text.Length - 1))); - int selectionLength = Math.Min(Text.Length - selectionStartIndex, selectedText.Length); - SetText(Text.Remove(selectionStartIndex, selectionLength)); - CaretIndex = Math.Min(Text.Length, selectionStartIndex); + int targetCaretIndex = Math.Max(0, Math.Min(selectionEndIndex, Math.Min(selectionStartIndex, Text.Length - 1))); + int selectionLength = Math.Min(Text.Length - targetCaretIndex, selectedText.Length); + SetText(Text.Remove(targetCaretIndex, selectionLength)); + CaretIndex = targetCaretIndex; ClearSelection(); OnTextChanged?.Invoke(this, Text); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs index 88bf466e4..8a495614b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs @@ -570,7 +570,7 @@ namespace Barotrauma AutoHideScrollBar = false, Visible = false }; - storeDailySpecialsGroup = CreateDealsGroup(storeBuyList); + storeDailySpecialsGroup = CreateDealsGroup(storeBuyList, CurrentLocation?.DailySpecialsCount ?? 1); tabLists.Add(StoreTab.Buy, storeBuyList); storeSellList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform)) @@ -578,7 +578,7 @@ namespace Barotrauma AutoHideScrollBar = false, Visible = false }; - storeRequestedGoodGroup = CreateDealsGroup(storeSellList); + storeRequestedGoodGroup = CreateDealsGroup(storeSellList, CurrentLocation?.RequestedGoodsCount ?? 1); tabLists.Add(StoreTab.Sell, storeSellList); storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform)) @@ -586,7 +586,7 @@ namespace Barotrauma AutoHideScrollBar = false, Visible = false }; - storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList); + storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList, CurrentLocation?.RequestedGoodsCount ?? 1); tabLists.Add(StoreTab.SellSub, storeSellFromSubList); // Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------ @@ -713,8 +713,10 @@ namespace Barotrauma private LocalizedString GetPlayerBalanceText() => TextManager.FormatCurrency(PlayerWallet.Balance); - private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount = 4) + private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount) { + // Add 1 for the header + elementCount++; var elementHeight = (int)(GUI.yScale * 80); var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, elementCount * elementHeight + 3), parent: parentList.Content.RectTransform), style: null) { @@ -852,24 +854,20 @@ namespace Barotrauma FilterStoreItems(category, searchBox.Text); } - int prevDailySpecialCount; + int prevDailySpecialCount, prevRequestedGoodsCount, prevSubRequestedGoodsCount; private void RefreshStoreBuyList() { float prevBuyListScroll = storeBuyList.BarScroll; float prevShoppingCrateScroll = shoppingCrateBuyList.BarScroll; - bool hasPermissions = HasBuyPermissions; - HashSet existingItemFrames = new HashSet(); - int dailySpecialCount = ActiveStore.DailySpecials.Count; - if ((storeDailySpecialsGroup != null) != ActiveStore.DailySpecials.Any() || dailySpecialCount != prevDailySpecialCount) { if (storeDailySpecialsGroup == null || dailySpecialCount != prevDailySpecialCount) { storeBuyList.RemoveChild(storeDailySpecialsGroup?.Parent); - storeDailySpecialsGroup = CreateDealsGroup(storeBuyList, 1 + dailySpecialCount); + storeDailySpecialsGroup = CreateDealsGroup(storeBuyList, dailySpecialCount); storeDailySpecialsGroup.Parent.SetAsFirstChild(); } else @@ -881,6 +879,8 @@ namespace Barotrauma prevDailySpecialCount = dailySpecialCount; } + bool hasPermissions = HasTabPermissions(StoreTab.Sell); + var existingItemFrames = new HashSet(); foreach (PurchasedItem item in ActiveStore.Stock) { CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity); @@ -942,29 +942,30 @@ namespace Barotrauma { float prevSellListScroll = storeSellList.BarScroll; float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll; - bool hasPermissions = HasTabPermissions(StoreTab.Sell); - HashSet existingItemFrames = new HashSet(); - if ((storeRequestedGoodGroup != null) != ActiveStore.RequestedGoods.Any()) + int requestedGoodsCount = ActiveStore.RequestedGoods.Count; + if ((storeRequestedGoodGroup != null) != ActiveStore.RequestedGoods.Any() || requestedGoodsCount != prevRequestedGoodsCount) { - if (storeRequestedGoodGroup == null) + storeSellList.RemoveChild(storeRequestedGoodGroup?.Parent); + if (storeRequestedGoodGroup == null || requestedGoodsCount != prevRequestedGoodsCount) { - storeRequestedGoodGroup = CreateDealsGroup(storeSellList); + storeRequestedGoodGroup = CreateDealsGroup(storeSellList, requestedGoodsCount); storeRequestedGoodGroup.Parent.SetAsFirstChild(); } else { - storeSellList.RemoveChild(storeRequestedGoodGroup.Parent); storeRequestedGoodGroup = null; } storeSellList.RecalculateChildren(); + prevRequestedGoodsCount = requestedGoodsCount; } + bool hasPermissions = HasTabPermissions(StoreTab.Sell); + var existingItemFrames = new HashSet(); foreach (PurchasedItem item in itemsToSell) { CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity); } - foreach (var requestedGood in ActiveStore.RequestedGoods) { if (itemsToSell.Any(pi => pi.ItemPrefab == requestedGood)) { continue; } @@ -1009,6 +1010,7 @@ namespace Barotrauma removedItemFrames.AddRange(storeRequestedGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList()); } removedItemFrames.ForEach(f => f.RectTransform.Parent = null); + if (activeTab == StoreTab.Sell) { FilterStoreItems(); } SortItems(StoreTab.Sell); @@ -1020,29 +1022,30 @@ namespace Barotrauma { float prevSellListScroll = storeSellFromSubList.BarScroll; float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll; - bool hasPermissions = HasSellSubPermissions; - HashSet existingItemFrames = new HashSet(); - if ((storeRequestedSubGoodGroup != null) != ActiveStore.RequestedGoods.Any()) + int requestedGoodsCount = ActiveStore.RequestedGoods.Count; + if ((storeRequestedSubGoodGroup != null) != ActiveStore.RequestedGoods.Any() || requestedGoodsCount != prevSubRequestedGoodsCount) { - if (storeRequestedSubGoodGroup == null) + storeSellFromSubList.RemoveChild(storeRequestedSubGoodGroup?.Parent); + if (storeRequestedSubGoodGroup == null || requestedGoodsCount != prevSubRequestedGoodsCount) { - storeRequestedSubGoodGroup = CreateDealsGroup(storeSellList); + storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList, requestedGoodsCount); storeRequestedSubGoodGroup.Parent.SetAsFirstChild(); } else { - storeSellFromSubList.RemoveChild(storeRequestedSubGoodGroup.Parent); storeRequestedSubGoodGroup = null; } storeSellFromSubList.RecalculateChildren(); + prevSubRequestedGoodsCount = requestedGoodsCount; } + bool hasPermissions = HasSellSubPermissions; + var existingItemFrames = new HashSet(); foreach (PurchasedItem item in itemsToSellFromSub) { CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity); } - foreach (var requestedGood in ActiveStore.RequestedGoods) { if (itemsToSellFromSub.Any(pi => pi.ItemPrefab == requestedGood)) { continue; } @@ -1087,6 +1090,7 @@ namespace Barotrauma removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList()); } removedItemFrames.ForEach(f => f.RectTransform.Parent = null); + if (activeTab == StoreTab.SellSub) { FilterStoreItems(); } SortItems(StoreTab.SellSub); @@ -2164,6 +2168,10 @@ namespace Barotrauma { RefreshItemsToSellFromSub(); } + if (needsRefresh) + { + Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); + } if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy)) { RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs index 152424061..d4461b472 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs @@ -84,19 +84,16 @@ namespace Barotrauma private void Initialize() { initialized = true; + currentSubText = TextManager.Get("currentsub"); + deliveryFeeText = TextManager.Get("deliveryfee"); + deliveryText = TextManager.Get("requestdeliverybutton"); + switchText = TextManager.Get("switchtosubmarinebutton"); + purchaseAndSwitchText = TextManager.Get("purchaseandswitch"); + purchaseOnlyText = TextManager.Get("purchase"); + priceText = TextManager.Get("price"); if (transferService) { deliveryFee = CalculateDeliveryFee(); - currentSubText = TextManager.Get("currentsub"); - deliveryFeeText = TextManager.Get("deliveryfee"); - deliveryText = TextManager.Get("requestdeliverybutton"); - switchText = TextManager.Get("switchtosubmarinebutton"); - } - else - { - purchaseAndSwitchText = TextManager.Get("purchaseandswitch"); - purchaseOnlyText = TextManager.Get("purchase"); - priceText = TextManager.Get("price"); } currencyName = TextManager.Get("credit").Value.ToLowerInvariant(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs index a927e30a4..e54913559 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs @@ -305,10 +305,10 @@ namespace Barotrauma public static LocalizedString GetMoneyTransferVoteResultMessage(Client from, Client to, int transferAmount, int yesVoteCount, int noVoteCount, bool votePassed) { LocalizedString result = string.Empty; - if (from != null) + if (from == null && to != null) { result = TextManager.GetWithVariables(votePassed ? "crewwallet.banktoplayer.votepassed" : "crewwallet.banktoplayer.votefailed", - ("[playername]", from.Name), + ("[playername]", to.Name), ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", transferAmount)), ("[yesvotecount]", yesVoteCount.ToString()), ("[novotecount]", noVoteCount.ToString())); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index a046c051f..de83074a1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -472,7 +472,9 @@ namespace Barotrauma TitleScreen.LoadState = MathHelper.Lerp(min, max, progress.Value); yield return CoroutineStatus.Running; } - + + TextManager.VerifyLanguageAvailable(); + DebugConsole.Init(); #if !DEBUG && !OSX diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs index d00290a64..439e43f96 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs @@ -55,15 +55,17 @@ namespace Barotrauma { if (_isCrewMenuOpen == value) { return; } _isCrewMenuOpen = value; - #warning TODO: update GameSettings.CurrentConfig.CrewMenuOpen when round ends + PreferCrewMenuOpen = value; } } + public static bool PreferCrewMenuOpen = true; + public bool AutoShowCrewList() => _isCrewMenuOpen = true; public void AutoHideCrewList() => _isCrewMenuOpen = false; - public void ResetCrewList() => _isCrewMenuOpen = GameSettings.CurrentConfig.CrewMenuOpen; + public void ResetCrewList() => _isCrewMenuOpen = PreferCrewMenuOpen; const float CommandNodeAnimDuration = 0.2f; @@ -217,7 +219,7 @@ namespace Barotrauma screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); prevUIScale = GUI.Scale; - _isCrewMenuOpen = GameSettings.CurrentConfig.CrewMenuOpen; + _isCrewMenuOpen = PreferCrewMenuOpen = GameSettings.CurrentConfig.CrewMenuOpen; } public static void CreateReportButtons(CrewManager crewManager, GUIComponent parent, IReadOnlyList reports, bool isHorizontal) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs index 3c2dd322e..2e35a7dac 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -592,6 +592,13 @@ namespace Barotrauma selectedMissionIndices.Add(msg.ReadByte()); } + ushort ownedSubCount = msg.ReadUInt16(); + List ownedSubIndices = new List(); + for (int i = 0; i < ownedSubCount; i++) + { + ownedSubIndices.Add(msg.ReadUInt16()); + } + bool allowDebugTeleport = msg.ReadBoolean(); float? reputation = null; if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); } @@ -697,6 +704,17 @@ namespace Barotrauma campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex); campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); campaign.Map.SelectMission(selectedMissionIndices); + + GameMain.GameSession.OwnedSubmarines.Clear(); + foreach (int ownedSubIndex in ownedSubIndices) + { + SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex]; + if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned)) + { + GameMain.GameSession.OwnedSubmarines.Add(sub); + } + } + campaign.Map.AllowDebugTeleport = allowDebugTeleport; campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems); campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs index b5e745407..7af73e740 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs @@ -4,9 +4,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Reflection.Metadata; namespace Barotrauma.Items.Components { @@ -53,15 +51,12 @@ namespace Barotrauma.Items.Components [Serialize("vendingmachine.outofstock", IsPropertySaveable.Yes)] public string FabricationLimitReachedText { get; set; } - partial void InitProjSpecific() - { - //CreateGUI(); - } - protected override void OnResolutionChanged() { - base.OnResolutionChanged(); - OnItemLoadedProjSpecific(); + if (GuiFrame != null) + { + OnItemLoadedProjSpecific(); + } } protected override void CreateGUI() @@ -162,7 +157,7 @@ namespace Barotrauma.Items.Components // === ACTIVATE BUTTON === // var buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), inputArea.RectTransform), childAnchor: Anchor.CenterRight); activateButton = new GUIButton(new RectTransform(new Vector2(1f, 0.6f), buttonFrame.RectTransform), - TextManager.Get(CreateButtonText), style: "DeviceButton") + TextManager.Get(CreateButtonText), style: "DeviceButtonFixedSize") { OnClicked = StartButtonClicked, UserData = selectedItem, @@ -173,7 +168,7 @@ namespace Barotrauma.Items.Components { bottomFrame.RectTransform.RelativeSize = new Vector2(1.0f, 0.1f); activateButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), bottomFrame.RectTransform, Anchor.CenterRight), - TextManager.Get(CreateButtonText), style: "DeviceButton") + TextManager.Get(CreateButtonText), style: "DeviceButtonFixedSize") { OnClicked = StartButtonClicked, UserData = selectedItem, @@ -566,7 +561,7 @@ namespace Barotrauma.Items.Components LocalizedString itemName = GetRecipeNameAndAmount(selectedItem); LocalizedString name = itemName; - float quality = GetFabricatedItemQuality(selectedItem, user); + float quality = selectedItem.Quality ?? GetFabricatedItemQuality(selectedItem, user); if (quality > 0) { name = TextManager.GetWithVariable("itemname.quality" + (int)quality, "[itemname]", itemName + '\n') @@ -636,7 +631,7 @@ namespace Barotrauma.Items.Components float requiredTime = overrideRequiredTime ?? (user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user)); - if (requiredTime > 0.0f) + if ((int)requiredTime > 0) { new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), TextManager.Get("FabricatorRequiredTime") , textColor: ToolBox.GradientLerp(degreeOfSuccess, GUIStyle.Red, Color.Yellow, GUIStyle.Green), font: GUIStyle.SubHeadingFont) @@ -778,6 +773,12 @@ namespace Barotrauma.Items.Components UInt16 userID = msg.ReadUInt16(); Character user = Entity.FindEntityByID(userID) as Character; + ushort reachedLimitCount = msg.ReadUInt16(); + for (int i = 0; i < reachedLimitCount; i++) + { + fabricationLimits[msg.ReadUInt32()] = 0; + } + State = newState; if (newState == FabricatorState.Stopped || recipeHash == 0) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/StatusHUD.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/StatusHUD.cs index e956c1721..d6e5500c3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/StatusHUD.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/StatusHUD.cs @@ -320,7 +320,7 @@ namespace Barotrauma.Items.Components } } - GUI.DrawString(spriteBatch, hudPos, texts[0], textColors[0] * alpha, Color.Black * 0.7f * alpha, 2, GUIStyle.SubHeadingFont); + GUI.DrawString(spriteBatch, hudPos, texts[0].Value, textColors[0] * alpha, Color.Black * 0.7f * alpha, 2, GUIStyle.SubHeadingFont, ForceUpperCase.No); hudPos.X += 5.0f; hudPos.Y += 24.0f * GameSettings.CurrentConfig.Graphics.TextScale; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs index 3cc3610e0..a3a2867d0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs @@ -77,7 +77,7 @@ namespace Barotrauma get { return base.Rect; } set { - cachedVisibleSize = null; + cachedVisibleExtents = null; base.Rect = value; } } @@ -213,11 +213,11 @@ namespace Barotrauma UpdateSpriteStates(0.0f); } - private Vector2? cachedVisibleSize; + private Rectangle? cachedVisibleExtents; public void ResetCachedVisibleSize() { - cachedVisibleSize = null; + cachedVisibleExtents = null; } public override bool IsVisible(Rectangle worldView) @@ -234,28 +234,39 @@ namespace Barotrauma return false; } - Vector2 size; - if (cachedVisibleSize.HasValue) + Rectangle extents; + if (cachedVisibleExtents.HasValue) { - size = cachedVisibleSize.Value; + extents = cachedVisibleExtents.Value; } else { - float padding = 100.0f; - size = new Vector2(rect.Width + padding, rect.Height + padding); + int padding = 100; + + Vector2 min = new Vector2(-rect.Width / 2 - padding, -rect.Height / 2 - padding); + Vector2 max = -min; + foreach (IDrawableComponent drawable in drawableComponents) { - size.X = Math.Max(drawable.DrawSize.X, size.X); - size.Y = Math.Max(drawable.DrawSize.Y, size.Y); + min.X = Math.Min(min.X, -drawable.DrawSize.X / 2); + min.Y = Math.Min(min.Y, -drawable.DrawSize.Y / 2); + max.X = Math.Max(max.X, drawable.DrawSize.X / 2); + max.Y = Math.Max(max.Y, drawable.DrawSize.Y / 2); } - size *= 0.5f; - cachedVisibleSize = size; + foreach (DecorativeSprite decorativeSprite in Prefab.DecorativeSprites) + { + float scale = decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale; + min.X = Math.Min(-decorativeSprite.Sprite.size.X * decorativeSprite.Sprite.RelativeOrigin.X * scale, min.X); + min.Y = Math.Min(-decorativeSprite.Sprite.size.Y * (1.0f - decorativeSprite.Sprite.RelativeOrigin.Y) * scale, min.Y); + max.X = Math.Max(decorativeSprite.Sprite.size.X * (1.0f - decorativeSprite.Sprite.RelativeOrigin.X) * scale, max.X); + max.Y = Math.Max(decorativeSprite.Sprite.size.Y * decorativeSprite.Sprite.RelativeOrigin.Y * scale, max.Y); + } + cachedVisibleExtents = extents = new Rectangle(min.ToPoint(), max.ToPoint()); } - //cache world position so we don't need to calculate it 4 times Vector2 worldPosition = WorldPosition; - if (worldPosition.X - size.X > worldView.Right || worldPosition.X + size.X < worldView.X) return false; - if (worldPosition.Y + size.Y < worldView.Y - worldView.Height || worldPosition.Y - size.Y > worldView.Y) return false; + if (worldPosition.X + extents.X > worldView.Right || worldPosition.X + extents.Width < worldView.X) { return false; } + if (worldPosition.Y + extents.Height < worldView.Y - worldView.Height || worldPosition.Y + extents.Y > worldView.Y) { return false; } return true; } @@ -934,8 +945,8 @@ namespace Barotrauma { OnSelected = (component, userData) => { - string text = userData as string ?? ""; - AddTag(text); + if (!(userData is Identifier)) { return true; } + AddTag((Identifier)userData); textBox.Text = Tags; msgBox.Close(); return true; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs index 24f85d6bd..a36e4216b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs @@ -239,7 +239,7 @@ namespace Barotrauma } if (min.X > worldView.Right || max.X < worldView.X) { return false; } - if ( min.Y > worldView.Y || max.Y < worldView.Y - worldView.Height) { return false; } + if (min.Y > worldView.Y || max.Y < worldView.Y - worldView.Height) { return false; } return true; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs index 41ab884b8..c59c2d2a6 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Client.cs @@ -6,23 +6,6 @@ using System.Linq; namespace Barotrauma.Networking { - struct TempClient - { - public string Name; - public Identifier PreferredJob; - public CharacterTeamType PreferredTeam; - public UInt16 NameID; - public UInt64 SteamID; - public byte ID; - public UInt16 CharacterID; - public float Karma; - public bool Muted; - public bool InGame; - public bool HasPermissions; - public bool IsOwner; - public bool AllowKicking; - } - partial class Client : IDisposable { public VoipSound VoipSound @@ -50,6 +33,8 @@ namespace Barotrauma.Networking public bool AllowKicking; + public bool IsDownloading; + public float Karma; public void UpdateSoundPosition() diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 1d3c335a7..57ff660c5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -1899,44 +1899,6 @@ namespace Barotrauma.Networking if (gameStarted) { - string ownedSubmarineIndexes = inc.ReadString(); - if (ownedSubmarineIndexes != string.Empty) - { - string[] ownedIndexes = ownedSubmarineIndexes.Split(';'); - - if (GameMain.GameSession != null) - { - GameMain.GameSession.OwnedSubmarines = new List(); - for (int i = 0; i < ownedIndexes.Length; i++) - { - if (int.TryParse(ownedIndexes[i], out int index)) - { - SubmarineInfo sub = GameMain.Client.ServerSubmarines[index]; - if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned)) - { - GameMain.GameSession.OwnedSubmarines.Add(sub); - } - } - } - } - else - { - GameMain.NetLobbyScreen.ServerOwnedSubmarines = new List(); - for (int i = 0; i < ownedIndexes.Length; i++) - { - int index; - if (int.TryParse(ownedIndexes[i], out index)) - { - SubmarineInfo sub = GameMain.Client.ServerSubmarines[index]; - if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned)) - { - GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(sub); - } - } - } - } - } - if (Screen.Selected != GameMain.GameScreen) { new GUIMessageBox(TextManager.Get("PleaseWait"), TextManager.Get(allowSpectating ? "RoundRunningSpectateEnabled" : "RoundRunningSpectateDisabled")); @@ -1953,37 +1915,8 @@ namespace Barotrauma.Networking int clientCount = inc.ReadByte(); for (int i = 0; i < clientCount; i++) { - byte id = inc.ReadByte(); - UInt64 steamId = inc.ReadUInt64(); - UInt16 nameId = inc.ReadUInt16(); - string name = inc.ReadString(); - Identifier preferredJob = inc.ReadIdentifier(); - byte preferredTeam = inc.ReadByte(); - UInt16 characterID = inc.ReadUInt16(); - float karma = inc.ReadSingle(); - bool muted = inc.ReadBoolean(); - bool inGame = inc.ReadBoolean(); - bool hasPermissions = inc.ReadBoolean(); - bool isOwner = inc.ReadBoolean(); - bool allowKicking = inc.ReadBoolean() || IsServerOwner; + tempClients.Add(INetSerializableStruct.Read(inc)); inc.ReadPadBits(); - - tempClients.Add(new TempClient - { - ID = id, - NameID = nameId, - SteamID = steamId, - Name = name, - PreferredJob = preferredJob, - PreferredTeam = (CharacterTeamType)preferredTeam, - CharacterID = characterID, - Karma = karma, - Muted = muted, - InGame = inGame, - HasPermissions = hasPermissions, - IsOwner = isOwner, - AllowKicking = allowKicking - }); } if (NetIdUtils.IdMoreRecent(listId, LastClientListUpdateID)) @@ -2018,6 +1951,7 @@ namespace Barotrauma.Networking existingClient.InGame = tc.InGame; existingClient.IsOwner = tc.IsOwner; existingClient.AllowKicking = tc.AllowKicking; + existingClient.IsDownloading = tc.IsDownloading; GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient); if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterID > 0) { @@ -2584,7 +2518,7 @@ namespace Barotrauma.Networking switch (transfer.FileType) { case FileTransferType.Submarine: - new GUIMessageBox(TextManager.Get("ServerDownloadFinished"), TextManager.GetWithVariable("FileDownloadedNotification", "[filename]", transfer.FileName)); + //new GUIMessageBox(TextManager.Get("ServerDownloadFinished"), TextManager.GetWithVariable("FileDownloadedNotification", "[filename]", transfer.FileName)); var newSub = new SubmarineInfo(transfer.FilePath); if (newSub.IsFileCorrupted) { return; } @@ -2644,7 +2578,6 @@ namespace Barotrauma.Networking NetLobbyScreen.FailedSubInfo failedOwnedSub = GameMain.NetLobbyScreen.FailedOwnedSubs.Find(s => s.Name == newSub.Name && s.Hash == newSub.MD5Hash.StringRepresentation); if (failedOwnedSub != default) { - GameMain.NetLobbyScreen.ServerOwnedSubmarines.Add(newSub); GameMain.NetLobbyScreen.FailedOwnedSubs.Remove(failedOwnedSub); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs index e088e3b15..de7227cc8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs @@ -166,9 +166,12 @@ namespace Barotrauma int votes = inc.ReadByte(); string subName = inc.ReadString(); List serversubs = new List(); - foreach (GUIComponent item in GameMain.NetLobbyScreen?.SubList?.Content?.Children) + if (GameMain.NetLobbyScreen?.SubList?.Content != null) { - if (item.UserData != null && item.UserData is SubmarineInfo) { serversubs.Add(item.UserData as SubmarineInfo); } + foreach (GUIComponent item in GameMain.NetLobbyScreen.SubList.Content.Children) + { + if (item.UserData != null && item.UserData is SubmarineInfo) { serversubs.Add(item.UserData as SubmarineInfo); } + } } SubmarineInfo sub = serversubs.FirstOrDefault(s => s.Name == subName); SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Particles/ParticlePrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Particles/ParticlePrefab.cs index 519af766f..e758564c5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Particles/ParticlePrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Particles/ParticlePrefab.cs @@ -271,7 +271,10 @@ namespace Barotrauma.Particles StartRotationMax = StartRotationMin; } - if (CollisionRadius <= 0.0f) CollisionRadius = Sprites.Count > 0 ? 1 : Sprites[0].SourceRect.Width / 2.0f; + if (CollisionRadius <= 0.0f && UseCollision) + { + CollisionRadius = Sprites.Count > 0 ? Sprites[0].SourceRect.Width / 2.0f : 1; + } } public Vector2 CalculateEndPosition(Vector2 startPosition, Vector2 velocity) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs index a1bbea8ab..b9aed65dd 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs @@ -1048,7 +1048,7 @@ namespace Barotrauma box.Content.ChildAnchor = Anchor.TopCenter; box.Content.AbsoluteSpacing = 20; int elementSize = 30; - var listBox = new GUIListBox(new RectTransform(new Vector2(1, 0.9f), box.Content.RectTransform)); + var listBox = new GUIListBox(new RectTransform(new Vector2(1, 0.75f), box.Content.RectTransform)); new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform), TextManager.Get("leveleditor.levelobjname")) { CanBeFocused = false }; @@ -1059,13 +1059,13 @@ namespace Barotrauma var texturePathBox = new GUITextBox(new RectTransform(new Point(listBox.Content.Rect.Width, elementSize), listBox.Content.RectTransform)); foreach (LevelObjectPrefab prefab in LevelObjectPrefab.Prefabs) { - if (prefab.Sprites.FirstOrDefault() == null) continue; + if (prefab.Sprites.FirstOrDefault() == null) { continue; } texturePathBox.Text = Path.GetDirectoryName(prefab.Sprites.FirstOrDefault().FilePath.Value); break; } //this is nasty :( - newPrefab = new LevelObjectPrefab(null, null, Identifier.Empty); + newPrefab = new LevelObjectPrefab(null, null, new Identifier("No identifier")); new SerializableEntityEditor(listBox.Content.RectTransform, newPrefab, false, false); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs index f8468ea3c..97398b63d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs @@ -828,7 +828,6 @@ namespace Barotrauma " -playstyle " + ((PlayStyle)playstyleBanner.UserData).ToString() + " -banafterwrongpassword " + wrongPasswordBanBox.Selected.ToString() + " -karmaenabled " + (!karmaBox.Selected).ToString() + - " -karmapreset default" + " -maxplayers " + maxPlayersBox.Text; if (!string.IsNullOrWhiteSpace(passwordBox.Text)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs index 681edc43a..c07116c26 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/ModDownloadScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using Barotrauma.Extensions; using Barotrauma.IO; using Barotrauma.Networking; +using Barotrauma.Steam; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Steamworks.Data; @@ -94,7 +95,7 @@ namespace Barotrauma } GUIMessageBox msgBox = new GUIMessageBox( - TextManager.Get("WorkshopItemDownloadTitle"), + TextManager.Get("ModDownloadTitle"), "", Array.Empty(), relativeSize: (0.5f, 0.75f)); @@ -122,8 +123,6 @@ namespace Barotrauma return tb; } - var title = textBlock(TextManager.Get("ModDownloadTitle"), GUIStyle.SubHeadingFont, Alignment.Center); - innerLayoutSpacing(0.05f); var header = textBlock(TextManager.Get("ModDownloadHeader"), GUIStyle.Font); innerLayoutSpacing(0.05f); @@ -138,8 +137,8 @@ namespace Barotrauma void buttonContainerSpacing(float width) => new GUIFrame(new RectTransform((width, 1.0f), buttonContainer.RectTransform), style: null); - void button(LocalizedString text, Action action) - => new GUIButton(new RectTransform((0.3f, 1.0f), buttonContainer.RectTransform), text) + void button(LocalizedString text, Action action, float width = 0.3f) + => new GUIButton(new RectTransform((width, 1.0f), buttonContainer.RectTransform), text) { OnClicked = (_, __) => { @@ -159,6 +158,28 @@ namespace Barotrauma }); buttonContainerSpacing(0.1f); + var missingIds = missingPackages.Where( + mp => mp.WorkshopId != 0 + && ContentPackageManager.WorkshopPackages.All(wp + => wp.SteamWorkshopId != mp.WorkshopId)) + .Select(mp => mp.WorkshopId) + .ToArray(); + if (missingIds.Any()) + { + buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), innerLayout.RectTransform), isHorizontal: true); + buttonContainerSpacing(0.15f); + button(TextManager.Get("SubscribeToAllOnWorkshop"), () => + { + BulkDownloader.SubscribeToServerMods(missingIds, + rejoinEndpoint: GameMain.Client.ClientPeer.ServerConnection.EndPointString, + rejoinLobby: SteamManager.CurrentLobbyID, + rejoinServerName: GameMain.NetLobbyScreen.ServerName.Text); + GameMain.Client.Disconnect(); + GameMain.MainMenuScreen.Select(); + }, width: 0.7f); + buttonContainerSpacing(0.15f); + } + foreach (var p in missingPackages) { pendingDownloads.Enqueue(p); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index a4cf42674..b3949ac46 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -218,9 +218,6 @@ namespace Barotrauma public MultiPlayerCampaignSetupUI CampaignSetupUI; - // Passed onto the gamesession when created - public List ServerOwnedSubmarines = new List(); - public bool UsingShuttle { get { return shuttleTickBox.Selected; } @@ -2007,7 +2004,7 @@ namespace Barotrauma SelectedTextColor = Color.Black, UserData = client }; - var soundIcon = new GUIImage(new RectTransform(new Point((int)(textBlock.Rect.Height * 0.8f)), textBlock.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) }, + var soundIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, textBlock.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, sprite: GUIStyle.GetComponentStyle("GUISoundIcon").GetDefaultSprite(), scaleToFit: true) { UserData = new Pair("soundicon", 0.0f), @@ -2017,7 +2014,7 @@ namespace Barotrauma HoverColor = Color.White }; - new GUIImage(new RectTransform(new Point((int)(textBlock.Rect.Height * 0.8f)), textBlock.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) }, + var soundIconDisabled = new GUIImage(new RectTransform(Vector2.One * 0.8f, textBlock.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, "GUISoundIconDisabled") { UserData = "soundicondisabled", @@ -2026,15 +2023,55 @@ namespace Barotrauma OverrideState = GUIComponent.ComponentState.None, HoverColor = Color.White }; - new GUIFrame(new RectTransform(new Vector2(0.6f, 0.6f), textBlock.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(10 + soundIcon.Rect.Width, 0) }, style: "GUIReadyToStart") + + var readyTick = new GUIFrame(new RectTransform(new Vector2(0.6f, 0.6f), textBlock.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(10 + soundIcon.Rect.Width, 0) }, style: "GUIReadyToStart") { Visible = false, CanBeFocused = false, ToolTip = TextManager.Get("ReadyToStartTickBox"), UserData = "clientready" }; + + var downloadingThrobber = new GUICustomComponent( + new RectTransform(Vector2.One, textBlock.RectTransform, scaleBasis: ScaleBasis.BothHeight), + onUpdate: null, + onDraw: DrawDownloadThrobber(client, soundIcon, soundIconDisabled, readyTick)); } + private Action DrawDownloadThrobber(Client client, params GUIComponent[] otherComponents) + => (sb, c) => DrawDownloadThrobber(client, otherComponents, sb, c); //poor man's currying + + private void DrawDownloadThrobber(Client client, GUIComponent[] otherComponents, SpriteBatch spriteBatch, GUICustomComponent component) + { + if (!client.IsDownloading) + { + component.ToolTip = ""; + return; + } + + component.HideElementsOutsideFrame = false; + int drawRectX = otherComponents.Where(c => c.Visible) + .Select(c => c.Rect) + .Concat(new Rectangle(component.Parent.Rect.Right, component.Parent.Rect.Y, 0, component.Parent.Rect.Height).ToEnumerable()) + .Min(r => r.X) - component.Parent.Rect.Height - 10; + Rectangle drawRect + = new Rectangle(drawRectX, component.Rect.Y, component.Parent.Rect.Height, component.Parent.Rect.Height); + component.RectTransform.AbsoluteOffset = drawRect.Location - component.Parent.Rect.Location; + component.RectTransform.NonScaledSize = drawRect.Size; + var sheet = GUIStyle.GenericThrobber; + sheet.Draw( + spriteBatch, + pos: drawRect.Location.ToVector2(), + spriteIndex: (int)Math.Floor(Timing.TotalTime * 24.0f) % sheet.FrameCount, + color: Color.White, + origin: Vector2.Zero, rotate: 0.0f, + scale: Vector2.One * component.Parent.Rect.Height / sheet.FrameSize.ToVector2()); + if (component.ToolTip.IsNullOrEmpty()) + { + component.ToolTip = TextManager.Get("PlayerIsDownloadingFiles"); + } + } + public void SetPlayerNameAndJobPreference(Client client) { var playerFrame = (GUITextBlock)PlayerList.Content.FindChild(client); @@ -2160,17 +2197,16 @@ namespace Barotrauma Steamworks.SteamFriends.OpenWebOverlay($"https://steamcommunity.com/profiles/{client.SteamID}"); })); - options.Add(new ContextMenuOption("ModerationMenu.UserDetails", isEnabled: true, onSelected: delegate + options.Add(new ContextMenuOption("ModerationMenu.ManagePlayer", isEnabled: true, onSelected: delegate { GameMain.NetLobbyScreen?.SelectPlayer(client); })); - // Creates sub context menu options for all the ranks - List permissionOptions = new List(); + List rankOptions = new List(); foreach (PermissionPreset rank in PermissionPreset.List) { - permissionOptions.Add(new ContextMenuOption(rank.Name, isEnabled: true, onSelected: () => + rankOptions.Add(new ContextMenuOption(rank.Name, isEnabled: true, onSelected: () => { LocalizedString label = TextManager.GetWithVariables(rank.Permissions == ClientPermissions.None ? "clearrankprompt" : "giverankprompt", ("[user]", client.Name), ("[rank]", rank.Name)); GUIMessageBox msgBox = new GUIMessageBox(string.Empty, label, new[] { TextManager.Get("Yes"), TextManager.Get("Cancel") }); @@ -2190,7 +2226,7 @@ namespace Barotrauma }) { Tooltip = rank.Description }); } - options.Add(new ContextMenuOption("Permissions", isEnabled: canPromo, options: permissionOptions.ToArray())); + options.Add(new ContextMenuOption("Rank", isEnabled: canPromo, options: rankOptions.ToArray())); Color clientColor = client.Character?.Info?.Job.Prefab.UIColor ?? Color.White; @@ -3554,12 +3590,6 @@ namespace Barotrauma ("[serverhash]", Md5Hash.GetShortHash(md5Hash))) + " "; } - //already showing a message about the same sub - if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "request" + subName)) - { - return false; - } - if (GameMain.Client.ServerSettings.AllowFileTransfers) { GameMain.Client?.RequestFile(FileTransferType.Submarine, subName, md5Hash); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index 05723864f..4d72c0d65 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -793,7 +793,7 @@ namespace Barotrauma showEntitiesPanel.RectTransform.NonScaledSize = new Point( - (int)(paddedShowEntitiesPanel.RectTransform.Children.Max(c => (int)((c.GUIComponent as GUITickBox)?.TextBlock.TextSize.X ?? 0)) / paddedShowEntitiesPanel.RectTransform.RelativeSize.X), + (int)Math.Max(showEntitiesPanel.RectTransform.NonScaledSize.X, paddedShowEntitiesPanel.RectTransform.Children.Max(c => (int)((c.GUIComponent as GUITickBox)?.TextBlock.TextSize.X ?? 0)) / paddedShowEntitiesPanel.RectTransform.RelativeSize.X), (int)(paddedShowEntitiesPanel.RectTransform.Children.Sum(c => c.MinSize.Y) / paddedShowEntitiesPanel.RectTransform.RelativeSize.Y)); GUITextBlock.AutoScaleAndNormalize(paddedShowEntitiesPanel.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock)); @@ -1700,44 +1700,13 @@ namespace Barotrauma return false; } - string specialSavePath = ""; if (MainSub.Info.Type != SubmarineType.Player) { - Identifier typeIdentifier = MainSub.Info.Type.ToString().ToIdentifier(); - Type contentType = ContentFile.Types.FirstOrDefault(t - => !t.Type.IsAbstract - && t.Type.IsSubclassOf(typeof(BaseSubFile)) - && t.Names.Contains(typeIdentifier)) - ?.Type ?? - typeof(SubmarineFile); if (MainSub.Info.Type == SubmarineType.OutpostModule && MainSub.Info.OutpostModuleInfo != null) { - contentType = typeof(OutpostModuleFile); MainSub.Info.PreviewImage = null; } - - if (contentType != typeof(SubmarineFile)) - { -#if DEBUG - var existingFiles = GameMain.VanillaContent.GetFiles(contentType); - if (contentType == typeof(OutpostModuleFile)) - { - existingFiles = existingFiles.Where(f => f.Path.Value.Contains("Ruin") == MainSub.Info.OutpostModuleInfo.ModuleFlags.Contains("ruin".ToIdentifier())); - } -#else - var existingFiles = ContentPackageManager.EnabledPackages.All - .Where(c => c != GameMain.VanillaContent) - .SelectMany(c => c.GetFiles(contentType)); -#endif - specialSavePath = existingFiles.FirstOrDefault(f => - ContentPackage.PathAllowedAsLocalModFile(f.Path.Value))?.Path.Value; - - if (!string.IsNullOrEmpty(specialSavePath)) - { - specialSavePath = Path.GetDirectoryName(specialSavePath); - } - } } else if (MainSub.Info.SubmarineClass == SubmarineClass.Undefined && !MainSub.Info.HasTag(SubmarineTag.Shuttle)) { @@ -1758,35 +1727,7 @@ namespace Barotrauma return true; } - if (!string.IsNullOrEmpty(specialSavePath) && - (string.IsNullOrEmpty(MainSub?.Info.FilePath) || Path.GetFileNameWithoutExtension(MainSub.Info.Name) != nameBox.Text || Path.GetDirectoryName(MainSub?.Info.FilePath) != specialSavePath)) - { - string submarineTypeTag = $"SubmarineType.{MainSub.Info.Type}"; - if (MainSub.Info.Type == SubmarineType.EnemySubmarine && !TextManager.ContainsTag(submarineTypeTag)) - { - submarineTypeTag = "MissionType.Pirate"; - } - var msgBox = new GUIMessageBox("", TextManager.GetWithVariables("savesubtospecialfolderprompt", - ("[type]", TextManager.Get(submarineTypeTag)), ("[outpostpath]", specialSavePath)), - new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") }); - msgBox.Buttons[0].OnClicked = (bt, userdata) => - { - SaveSubToFile(nameBox.Text, specialSavePath); - saveFrame = null; - msgBox.Close(); - return true; - }; - msgBox.Buttons[1].OnClicked = (bt, userdata) => - { - SaveSubToFile(nameBox.Text); - saveFrame = null; - msgBox.Close(); - return true; - }; - return true; - } - - var result = SaveSubToFile(nameBox.Text, specialSavePath); + var result = SaveSubToFile(nameBox.Text); saveFrame = null; return result; } @@ -1798,13 +1739,9 @@ namespace Barotrauma if (p is null) { return; } if (!packageReloadQueue.Contains(p)) { packageReloadQueue.Enqueue(p); } } - - private bool SaveSubToFile(string name, string specialSavePath = null) - { - bool canModifyPackage(ContentPackage p) - => p != null && ContentPackageManager.LocalPackages.Contains(p) && p != ContentPackageManager.VanillaCorePackage; - Type subFileType = MainSub?.Info.Type switch + public static Type DetermineSubFileType(SubmarineType type) + => type switch { SubmarineType.Outpost => typeof(OutpostFile), SubmarineType.OutpostModule => typeof(OutpostModuleFile), @@ -1812,8 +1749,16 @@ namespace Barotrauma SubmarineType.Wreck => typeof(WreckFile), SubmarineType.BeaconStation => typeof(BeaconStationFile), SubmarineType.EnemySubmarine => typeof(EnemySubmarineFile), - SubmarineType.Player => typeof(SubmarineFile) + SubmarineType.Player => typeof(SubmarineFile), + _ => null }; + + private bool SaveSubToFile(string name) + { + bool canModifyPackage(ContentPackage p) + => p != null && ContentPackageManager.LocalPackages.Contains(p) && p != ContentPackageManager.VanillaCorePackage; + + Type subFileType = DetermineSubFileType(MainSub?.Info.Type ?? SubmarineType.Player); void addSubAndSaveModProject(ModProject modProject, string filePath, string packagePath) { @@ -1858,11 +1803,13 @@ namespace Barotrauma foreach (var illegalChar in Path.GetInvalidFileNameChars()) { - if (!name.Contains(illegalChar)) continue; + if (!name.Contains(illegalChar)) { continue; } GUI.AddMessage(TextManager.GetWithVariable("SubNameIllegalCharsWarning", "[illegalchar]", illegalChar.ToString()), GUIStyle.Red); return false; } + name = name.Trim(); + string newLocalModDir = $"{ContentPackage.LocalModsDir}/{name}"; var vanilla = GameMain.VanillaContent; @@ -1871,33 +1818,7 @@ namespace Barotrauma string savePath = name + ".sub"; string prevSavePath = null; - if (!string.IsNullOrEmpty(specialSavePath)) - { - string directoryName = specialSavePath; - savePath = Path.Combine(directoryName, savePath); - ContentPackage contentPackage = ContentPackageManager.EnabledPackages.All.FirstOrDefault(cp => cp.Files.Any(f => Path.GetDirectoryName(f.Path.Value) == directoryName)); - - if (!contentPackage.Files.Any(f => f.Path == savePath) && canModifyPackage(contentPackage)) - { - var msgBox = new GUIMessageBox("", TextManager.GetWithVariable("addtocontentpackageprompt", "[packagename]", contentPackage.Name), - new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") }); - msgBox.Buttons[0].OnClicked = (bt, userdata) => - { - ModProject modProject = new ModProject(contentPackage); - addSubAndSaveModProject(modProject, savePath, contentPackage.Path); - EnqueueForReload(contentPackage); - - msgBox.Close(); - return true; - }; - msgBox.Buttons[1].OnClicked = (bt, userdata) => - { - msgBox.Close(); - return true; - }; - } - } - else if (!string.IsNullOrEmpty(MainSub?.Info.FilePath) && + if (!string.IsNullOrEmpty(MainSub?.Info.FilePath) && MainSub.Info.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) { prevSavePath = MainSub.Info.FilePath.CleanUpPath(); @@ -2515,7 +2436,7 @@ namespace Barotrauma new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform), TextManager.Get("SubPreviewImage"), font: GUIStyle.SubHeadingFont); - var previewImageHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), rightColumn.RectTransform), style: null) { Color = Color.Black, CanBeFocused = false }; + var previewImageHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f), rightColumn.RectTransform), style: null) { Color = Color.Black, CanBeFocused = false }; previewImage = new GUIImage(new RectTransform(Vector2.One, previewImageHolder.RectTransform), MainSub?.Info.PreviewImage, scaleToFit: true); previewImageButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; @@ -2567,7 +2488,7 @@ namespace Barotrauma previewImageButtonHolder.RectTransform.MinSize = new Point(0, previewImageButtonHolder.RectTransform.Children.Max(c => c.MinSize.Y)); - var horizontalArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.35f), rightColumn.RectTransform), style: null); + var horizontalArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.45f), rightColumn.RectTransform), style: null); var settingsLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), horizontalArea.RectTransform), TextManager.Get("SaveSubDialogSettings"), wrap: true, font: GUIStyle.SmallFont); @@ -2613,11 +2534,30 @@ namespace Barotrauma }; } - var contentPackagesLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), horizontalArea.RectTransform, Anchor.TopRight), + var contentPackagesLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), + horizontalArea.RectTransform, Anchor.BottomRight)) + { + Stretch = true + }; + + var contentPackagesLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), contentPackagesLayout.RectTransform), TextManager.Get("RequiredContentPackages"), wrap: true, font: GUIStyle.SmallFont); + contentPackagesLabel.RectTransform.MinSize + = GUIStyle.SmallFont.MeasureString(contentPackagesLabel.WrappedText).ToPoint(); - var contentPackList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f - contentPackagesLabel.RectTransform.RelativeSize.Y), - horizontalArea.RectTransform, Anchor.BottomRight)); + var contentPackList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), + contentPackagesLayout.RectTransform)); + + var contentPackFilter + = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.0f), contentPackagesLayout.RectTransform), + createClearButton: true); + contentPackFilter.OnTextChanged += (box, text) => + { + contentPackList.Content.Children.ForEach(c + => c.Visible = !(c is GUITickBox tb && + !tb.Text.Contains(text, StringComparison.OrdinalIgnoreCase))); + return true; + }; if (MainSub != null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs index 177cb11a2..ee74766d9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Barotrauma.IO; using System.Linq; +using System.Threading; using System.Xml.Linq; namespace Barotrauma @@ -479,6 +480,36 @@ namespace Barotrauma return sound.Play(volume ?? sound.BaseGain, far, freqMult ?? 1.0f, position, muffle: muffle); } + public static void DisposeDisabledMusic() + { + bool musicDisposed = false; + for (int i = 0; i < currentMusic.Length; i++) + { + var music = currentMusic[i]; + if (music is null) { continue; } + + if (!SoundPrefab.Prefabs.Contains(music)) + { + musicChannel[i].Dispose(); + musicDisposed = true; + currentMusic[i] = null; + } + } + + for (int i = 0; i < targetMusic.Length; i++) + { + var music = targetMusic[i]; + if (music is null) { continue; } + + if (!SoundPrefab.Prefabs.Contains(music)) + { + targetMusic[i] = null; + } + } + + if (musicDisposed) { Thread.Sleep(60); } + } + private static void UpdateMusic(float deltaTime) { if (musicClips == null || (GameMain.SoundManager?.Disabled ?? true)) { return; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs index 8d510a115..14b0b68e2 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPrefab.cs @@ -121,6 +121,7 @@ namespace Barotrauma } if (prefabSelectors.ContainsKey(p.ElementName)) { prefabSelectors[p.ElementName].RemoveIfContains(p); } UpdateSoundsWithTag(); + SoundPlayer.DisposeDisabledMusic(); }, onSort: () => { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/BulkDownloader.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/BulkDownloader.cs new file mode 100644 index 000000000..0542698d4 --- /dev/null +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/BulkDownloader.cs @@ -0,0 +1,125 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Barotrauma.Extensions; +using Barotrauma.Networking; +using Microsoft.Xna.Framework; + +namespace Barotrauma.Steam +{ + public static class BulkDownloader + { + public static void PrepareUpdates() + { + GUIMessageBox msgBox = new GUIMessageBox(headerText: "", text: TextManager.Get("DeterminingRequiredModUpdates"), + buttons: Array.Empty()); + TaskPool.Add( + "BulkDownloader.PrepareUpdates > GetItemsThatNeedUpdating", + GetItemsThatNeedUpdating(), + t => + { + msgBox.Close(); + if (!t.TryGetResult(out IReadOnlyList items)) { return; } + + InitiateDownloads(items); + }); + } + + internal static void SubscribeToServerMods(IEnumerable missingIds, string rejoinEndpoint, ulong rejoinLobby, string rejoinServerName) + { + GUIMessageBox msgBox = new GUIMessageBox(headerText: "", text: TextManager.Get("PreparingWorkshopDownloads"), + buttons: Array.Empty()); + TaskPool.Add( + "BulkDownloader.SubscribeToServerMods > GetItems", + Task.WhenAll(missingIds.Select(SteamManager.Workshop.GetItem)), + t => + { + msgBox.Close(); + if (!t.TryGetResult(out Steamworks.Ugc.Item?[] itemsNullable)) { return; } + + var items = itemsNullable + .Where(it => it.HasValue) + .Select(it => it ?? default) + .ToArray(); + + items.ForEach(it => it.Subscribe()); + InitiateDownloads(items, onComplete: () => + { + ContentPackageManager.UpdateContentPackageList(); + GameMain.Instance.ConnectEndpoint = rejoinEndpoint; + GameMain.Instance.ConnectLobby = rejoinLobby; + GameMain.Instance.ConnectName = rejoinServerName; + }); + }); + } + + private static async Task> GetItemsThatNeedUpdating() + { + var determiningTasks = ContentPackageManager.WorkshopPackages.Select(async p => (p, await p.IsUpToDate())); + (ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks); + + return (await Task.WhenAll(outOfDatePackages.Where(p => !p.IsUpToDate) + .Select(async p => await SteamManager.Workshop.GetItem(p.Package.SteamWorkshopId)))) + .Where(p => p.HasValue).Select(p => p ?? default).ToArray(); + } + + public static void InitiateDownloads(IReadOnlyList itemsToDownload, Action? onComplete = null) + { + var msgBox = new GUIMessageBox(TextManager.Get("WorkshopItemDownloading"), "", relativeSize: (0.5f, 0.6f), + buttons: new LocalizedString[] { TextManager.Get("Cancel") }); + msgBox.Buttons[0].OnClicked = msgBox.Close; + var modsList = new GUIListBox(new RectTransform((1.0f, 0.8f), msgBox.Content.RectTransform)) + { + HoverCursor = CursorState.Default + }; + foreach (var item in itemsToDownload) + { + var itemFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), modsList.Content.RectTransform), + style: null) + { + CanBeFocused = false + }; + var itemTitle = new GUITextBlock(new RectTransform(Vector2.One, itemFrame.RectTransform), + text: item.Title); + var itemDownloadProgress + = new GUIProgressBar(new RectTransform((0.5f, 0.75f), + itemFrame.RectTransform, Anchor.CenterRight), 0.0f) + { + Color = GUIStyle.Green + }; + itemDownloadProgress.ProgressGetter = () => + { + float progress = 0.0f; + if (item.IsDownloading) { progress = item.DownloadAmount; } + else if (itemDownloadProgress.BarSize > 0.0f) { progress = 1.0f; } + + return Math.Max(itemDownloadProgress.BarSize, + MathHelper.Lerp(itemDownloadProgress.BarSize, progress, 0.05f)); + }; + } + TaskPool.Add("DownloadItems", DownloadItems(itemsToDownload, msgBox), _ => + { + if (GUIMessageBox.MessageBoxes.Contains(msgBox)) + { + onComplete?.Invoke(); + } + msgBox.Close(); + if (SettingsMenu.Instance?.WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu) + { + mutableWorkshopMenu.PopulateInstalledModLists(); + } + }); + } + + private static async Task DownloadItems(IReadOnlyList itemsToDownload, GUIMessageBox msgBox) + { + foreach (var item in itemsToDownload) + { + await SteamManager.Workshop.Reinstall(item); + if (!GUIMessageBox.MessageBoxes.Contains(msgBox)) { break; } + } + } + } +} diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs index f94a64186..5b87ab120 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs @@ -1,10 +1,15 @@ #nullable enable +using System; +using Barotrauma.Extensions; using Microsoft.Xna.Framework; namespace Barotrauma.Steam { sealed class ImmutableWorkshopMenu : WorkshopMenu { + private readonly GUIListBox regularList; + private readonly GUITextBox filterBox; + public ImmutableWorkshopMenu(GUIFrame parent) : base(parent) { var mainLayout @@ -20,8 +25,8 @@ namespace Barotrauma.Steam coreBox.TextBlock.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f); Label(mainLayout, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont); - var regularList = new GUIListBox( - NewItemRectT(mainLayout, heightScale: 12f)) + regularList = new GUIListBox( + NewItemRectT(mainLayout, heightScale: 11f)) { OnSelected = (component, o) => false, HoverCursor = CursorState.Default @@ -31,11 +36,22 @@ namespace Barotrauma.Steam var regularBox = new GUITextBlock( new RectTransform((1.0f, 0.07f), regularList.Content.RectTransform), text: p.Name) { - CanBeFocused = false + CanBeFocused = false, + UserData = p }; } + filterBox = CreateSearchBox(mainLayout, width: 1.0f); Label(mainLayout, TextManager.Get("CannotChangeMods"), GUIStyle.Font); } + + protected override void UpdateModListItemVisibility() + { + string str = filterBox.Text; + regularList.Content.Children + .ForEach(c => c.Visible = str.IsNullOrWhiteSpace() + || (c.UserData is ContentPackage p + && p.Name.Contains(str, StringComparison.OrdinalIgnoreCase))); + } } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs index a66e87f55..4e01d5ece 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs @@ -32,6 +32,7 @@ namespace Barotrauma.Steam private readonly GUIListBox disabledRegularModsList; private readonly Action onInstalledInfoButtonHit; private readonly GUITextBox modsListFilter; + private readonly GUIButton bulkUpdateButton; private CancellationTokenSource taskCancelSrc = new CancellationTokenSource(); private readonly HashSet itemThumbnails = new HashSet(); @@ -55,7 +56,8 @@ namespace Barotrauma.Steam out enabledRegularModsList, out disabledRegularModsList, out onInstalledInfoButtonHit, - out modsListFilter); + out modsListFilter, + out bulkUpdateButton); CreatePopularModsTab(out popularModsList); CreatePublishTab(out selfModsList); @@ -179,13 +181,35 @@ namespace Barotrauma.Steam to.BarScroll *= (oldCount / newCount); } } + + private Action? currentSwapFunc = null; + + private void SetSwapFunc(GUIListBox from, GUIListBox to) + { + currentSwapFunc = () => + { + to.Deselect(); + var selected = from.AllSelected.ToArray(); + foreach (var frame in selected) + { + frame.Parent.RemoveChild(frame); + frame.RectTransform.Parent = to.Content.RectTransform; + } + from.RecalculateChildren(); + from.RectTransform.RecalculateScale(true); + to.RecalculateChildren(); + to.RectTransform.RecalculateScale(true); + to.Select(selected); + }; + } private void CreateInstalledModsTab( out GUIDropDown enabledCoreDropdown, out GUIListBox enabledRegularModsList, out GUIListBox disabledRegularModsList, out Action onInstalledInfoButtonHit, - out GUITextBox modsListFilter) + out GUITextBox modsListFilter, + out GUIButton bulkUpdateButton) { GUIFrame content = CreateNewContentFrame(Tab.InstalledMods); @@ -196,49 +220,72 @@ namespace Barotrauma.Steam { if (itemOrPackage.TryGet(out Steamworks.Ugc.Item item)) { PopulateFrameWithItemInfo(item, selectedFrame); } }, - onDeselected: PopulateInstalledModLists, + onDeselected: () => PopulateInstalledModLists(), out onInstalledInfoButtonHit, out var deselect); GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(Vector2.One, outerContainer.Content.RectTransform), childAnchor: Anchor.TopCenter); mainLayout.RectTransform.SetAsFirstChild(); - GUILayoutGroup coreSelectionLayout = - new GUILayoutGroup(new RectTransform((0.5f, 0.15f), mainLayout.RectTransform)); - Label(coreSelectionLayout, TextManager.Get("enabledcore"), GUIStyle.SubHeadingFont, heightScale: 1.0f / 0.15f); - enabledCoreDropdown = Dropdown(coreSelectionLayout, + + var (topLeft, _, topRight) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.13f); + topLeft.Stretch = true; + Label(topLeft, TextManager.Get("enabledcore"), GUIStyle.SubHeadingFont, heightScale: 1.0f); + enabledCoreDropdown = Dropdown(topLeft, (p) => p.Name, ContentPackageManager.CorePackages.ToArray(), ContentPackageManager.EnabledPackages.Core!, (p) => { }, - heightScale: 1.0f / 0.15f); + heightScale: 1.0f / 13.0f); + Label(topLeft, "", GUIStyle.SubHeadingFont, heightScale: 1.0f); + topRight.ChildAnchor = Anchor.CenterLeft; - var (left, center, right) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.78f); - right.ChildAnchor = Anchor.TopRight; - - Action swapFunc(GUIListBox from, GUIListBox to) + var topRightButtons = new GUILayoutGroup(new RectTransform((1.0f, 0.5f), topRight.RectTransform), + isHorizontal: true, childAnchor: Anchor.CenterLeft) { - return () => - { - to.Deselect(); - var selected = from.AllSelected.ToArray(); - foreach (var frame in selected) - { - frame.Parent.RemoveChild(frame); - frame.RectTransform.Parent = to.Content.RectTransform; - } - from.RecalculateChildren(); - from.RectTransform.RecalculateScale(true); - to.RecalculateChildren(); - to.RectTransform.RecalculateScale(true); - to.Select(selected); - }; + Stretch = true, + RelativeSpacing = 0.05f + }; + + void padTopRight(float width=1.0f) + { + new GUIFrame(new RectTransform((width, 1.0f), topRightButtons.RectTransform), style: null); } - Action? currentCenterCallback = null; + padTopRight(); + //TODO: put stuff here + padTopRight(width: 3.0f); + var refreshListsButton + = new GUIButton( + new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight), + text: "", style: "GUIReloadButton") + { + OnClicked = (b, o) => + { + PopulateInstalledModLists(); + return false; + }, + ToolTip = TextManager.Get("RefreshModLists") + }; + bulkUpdateButton + = new GUIButton( + new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight), + text: "", style: "GUIUpdateButton") + { + OnClicked = (b, o) => + { + BulkDownloader.PrepareUpdates(); + return false; + }, + Enabled = false + }; + padTopRight(width: 0.1f); + + var (left, center, right) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.8f); + right.ChildAnchor = Anchor.TopRight; //enabled mods Label(left, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont); - var enabledModsList = new GUIListBox(new RectTransform((1.0f, 0.92f), left.RectTransform)) + var enabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), left.RectTransform)) { CurrentDragMode = GUIListBox.DragMode.DragOutsideBox, CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple, @@ -248,7 +295,7 @@ namespace Barotrauma.Steam //disabled mods Label(right, TextManager.Get("disabledregular"), GUIStyle.SubHeadingFont); - var disabledModsList = new GUIListBox(new RectTransform((1.0f, 0.92f), right.RectTransform)) + var disabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), right.RectTransform)) { CurrentDragMode = GUIListBox.DragMode.DragOutsideBox, CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple, @@ -265,7 +312,7 @@ namespace Barotrauma.Steam Visible = false, OnClicked = (button, o) => { - currentCenterCallback?.Invoke(); + currentSwapFunc?.Invoke(); return false; } }; @@ -277,7 +324,7 @@ namespace Barotrauma.Steam centerButton.Visible = true; centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleRight")); - currentCenterCallback = swapFunc(enabledModsList, disabledModsList); + SetSwapFunc(enabledModsList, disabledModsList); return true; }; @@ -288,30 +335,12 @@ namespace Barotrauma.Steam centerButton.Visible = true; centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleLeft")); - currentCenterCallback = swapFunc(disabledModsList, enabledModsList); + SetSwapFunc(disabledModsList, enabledModsList); return true; }; - var searchRectT = NewItemRectT(mainLayout, heightScale: 1.0f); - searchRectT.RelativeSize = (0.5f, searchRectT.RelativeSize.Y); - var searchHolder = new GUIFrame(searchRectT, style: null); - var searchBox = new GUITextBox(new RectTransform(Vector2.One, searchHolder.RectTransform), "", createClearButton: true); - var searchTitle = new GUITextBlock(new RectTransform(Vector2.One, searchHolder.RectTransform) {Anchor = Anchor.TopLeft}, - textColor: Color.DarkGray * 0.6f, - text: TextManager.Get("Search") + "...", - textAlignment: Alignment.CenterLeft) - { - CanBeFocused = false - }; - searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; - searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = searchBox.Text.IsNullOrWhiteSpace(); }; - - searchBox.OnTextChanged += (sender, str) => - { - UpdateModListItemVisibility(); - return true; - }; + var searchBox = CreateSearchBox(mainLayout, width: 0.5f); modsListFilter = searchBox; new GUICustomComponent(new RectTransform(Vector2.Zero, content.RectTransform), @@ -319,6 +348,18 @@ namespace Barotrauma.Steam { HandleDraggingAcrossModLists(enabledModsList, disabledModsList); HandleDraggingAcrossModLists(disabledModsList, enabledModsList); + if (PlayerInput.PrimaryMouseButtonClicked() + && !GUI.IsMouseOn(enabledModsList) + && !GUI.IsMouseOn(disabledModsList) + && GUIContextMenu.CurrentContextMenu is null) + { + enabledModsList.Deselect(); + disabledModsList.Deselect(); + } + else if (!PlayerInput.IsCtrlDown() && !PlayerInput.IsShiftDown() && PlayerInput.DoubleClicked()) + { + currentSwapFunc?.Invoke(); + } }, onDraw: (spriteBatch, component) => { @@ -327,7 +368,7 @@ namespace Barotrauma.Steam }); } - private void UpdateModListItemVisibility() + protected override void UpdateModListItemVisibility() { string str = modsListFilter.Text; enabledRegularModsList.Content.Children.Concat(disabledRegularModsList.Content.Children) @@ -336,8 +377,21 @@ namespace Barotrauma.Steam && p.Name.Contains(str, StringComparison.OrdinalIgnoreCase))); } - private void PopulateInstalledModLists() + private void PrepareToShowModInfo(ContentPackage mod) { + TaskPool.Add($"PrepareToShow{mod.SteamWorkshopId}Info", SteamManager.Workshop.GetItem(mod.SteamWorkshopId), + t => + { + if (!t.TryGetResult(out Steamworks.Ugc.Item? item)) { return; } + if (item is null) { return; } + onInstalledInfoButtonHit(item.Value); + }); + } + + public void PopulateInstalledModLists(bool forceRefreshEnabled = false, bool refreshDisabled = true) + { + bulkUpdateButton.Enabled = false; + bulkUpdateButton.ToolTip = ""; ContentPackageManager.UpdateContentPackageList(); SwapDropdownValues(enabledCoreDropdown, @@ -353,6 +407,39 @@ namespace Barotrauma.Steam { UserData = mod }; + + var contextMenuHandler = new GUICustomComponent(new RectTransform(Vector2.Zero, modFrame.RectTransform), + onUpdate: (f, component) => + { + var parentList = modFrame.Parent?.Parent?.Parent as GUIListBox; //lovely jank :) + if (parentList is null) { return; } + if (GUI.MouseOn == modFrame && parentList.DraggedElement is null && PlayerInput.SecondaryMouseButtonClicked()) + { + if (!parentList.AllSelected.Contains(modFrame)) { parentList.Select(parentList.Content.GetChildIndex(modFrame)); } + static void noop() { } + + List contextMenuOptions = new List(); + if (ContentPackageManager.WorkshopPackages.Contains(mod)) + { + contextMenuOptions.Add( + new ContextMenuOption("ViewWorkshopModDetails".ToIdentifier(), isEnabled: true, onSelected: () => PrepareToShowModInfo(mod))); + } + + Identifier swapLabel + = ((parentList == enabledRegularModsList ? "Disable" : "Enable") + + (parentList.AllSelected.Count > 1 ? "SelectedWorkshopMods" : "WorkshopMod")) + .ToIdentifier(); + + contextMenuOptions.Add(new ContextMenuOption(swapLabel, + isEnabled: true, onSelected: currentSwapFunc ?? noop)); + + GUIContextMenu.CreateContextMenu( + pos: PlayerInput.MousePosition, + header: ToolBox.LimitString(mod.Name, GUIStyle.SubHeadingFont, GUI.IntScale(300f)), + headerColor: null, + contextMenuOptions.ToArray()); + } + }); var frameContent = new GUILayoutGroup(new RectTransform((0.95f, 0.9f), modFrame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) { @@ -392,20 +479,14 @@ namespace Barotrauma.Steam { var infoButton = new GUIButton( new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "", - style: "WorkshopMenu.InfoButton") + style: null) { + CanBeSelected = false, OnClicked = (button, o) => { - TaskPool.Add($"PrepareToShow{mod.SteamWorkshopId}Info", SteamManager.Workshop.GetItem(mod.SteamWorkshopId), - t => - { - if (!t.TryGetResult(out Steamworks.Ugc.Item? item)) { return; } - if (item is null) { return; } - onInstalledInfoButtonHit(item.Value); - }); + PrepareToShowModInfo(mod); return false; - }, - ToolTip = TextManager.Get("ViewModDetails") + } }; if (!SteamManager.IsInitialized) { @@ -420,27 +501,69 @@ namespace Barotrauma.Steam if (!isUpToDate) { + infoButton.CanBeSelected = true; infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]); infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable"); + bulkUpdateButton.Enabled = true; + bulkUpdateButton.ToolTip = TextManager.Get("ModUpdatesAvailable"); } }); } } + + void addRegularModsToList(IEnumerable mods, GUIListBox list) + { + list.ClearChildren(); + foreach (var mod in mods) + { + addRegularModToList(mod, list); + } + } + + var enabledMods = + (forceRefreshEnabled || (enabledRegularModsList.Content.CountChildren + disabledRegularModsList.Content.CountChildren == 0) + ? ContentPackageManager.EnabledPackages.Regular + : enabledRegularModsList.Content.Children + .Select(c => c.UserData) + .OfType() + .Where(p => ContentPackageManager.RegularPackages.Contains(p))) + .ToArray(); + var disabledMods = ContentPackageManager.RegularPackages.Where(p => !enabledMods.Contains(p)); - enabledRegularModsList.ClearChildren(); - for (int i = 0; i < ContentPackageManager.EnabledPackages.Regular.Count; i++) - { - var mod = ContentPackageManager.EnabledPackages.Regular[i]; - addRegularModToList(mod, enabledRegularModsList); - } + addRegularModsToList(enabledMods, enabledRegularModsList); + if (refreshDisabled) { addRegularModsToList(disabledMods, disabledRegularModsList); } - disabledRegularModsList.ClearChildren(); - foreach (var mod in ContentPackageManager.RegularPackages) - { - if (ContentPackageManager.EnabledPackages.Regular.Contains(mod)) { continue; } - addRegularModToList(mod, disabledRegularModsList); - } + TaskPool.Add( + $"DetermineWorkshopModIcons", + SteamManager.Workshop.GetPublishedItems(), + t => + { + if (!t.TryGetResult(out ISet items)) { return; } + var ids = items.Select(it => it.Id).ToHashSet(); + foreach (var child in enabledRegularModsList.Content.Children + .Concat(disabledRegularModsList.Content.Children)) + { + var mod = child.UserData as RegularPackage; + if (mod is null || !ContentPackageManager.WorkshopPackages.Contains(mod)) { continue; } + + var btn = child.GetChild()?.GetAllChildren().Last(); + if (btn is null) { continue; } + if (btn.Style != null) { continue; } + + btn.ApplyStyle( + GUIStyle.GetComponentStyle( + ids.Contains(mod.SteamWorkshopId) + ? "WorkshopMenu.PublishedIcon" + : "WorkshopMenu.DownloadedIcon")); + btn.ToolTip = TextManager.Get( + ids.Contains(mod.SteamWorkshopId) + ? "PublishedWorkshopMod" + : "DownloadedWorkshopMod"); + btn.HoverCursor = CursorState.Default; + } + }); + UpdateModListItemVisibility(); } @@ -468,7 +591,8 @@ namespace Barotrauma.Steam { ContentPackageManager.EnabledPackages.SetCore(EnabledCorePackage); ContentPackageManager.EnabledPackages.SetRegular(enabledRegularModsList.Content.Children - .Where(c => c.UserData is RegularPackage).Select(c => (RegularPackage)c.UserData).ToArray()); + .Select(c => c.UserData as RegularPackage).OfType().ToArray()); + PopulateInstalledModLists(forceRefreshEnabled: true, refreshDisabled: true); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs index 910fe6d1f..78a08a529 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs @@ -105,5 +105,29 @@ namespace Barotrauma.Steam protected GUIComponent CreateActionCarrier(GUIComponent parent, Identifier id, Action action) => new GUIFrame(new RectTransform(Vector2.Zero, parent.RectTransform), style: null) { UserData = new ActionCarrier(id, action) }; + + protected GUITextBox CreateSearchBox(GUILayoutGroup mainLayout, float width = 1.0f, float heightScale = 1.0f) + { + var searchRectT = NewItemRectT(mainLayout, heightScale: heightScale); + searchRectT.RelativeSize = (width, searchRectT.RelativeSize.Y); + var searchHolder = new GUIFrame(searchRectT, style: null); + var searchBox = new GUITextBox(new RectTransform(Vector2.One, searchHolder.RectTransform), "", createClearButton: true); + var searchTitle = new GUITextBlock(new RectTransform(Vector2.One, searchHolder.RectTransform) {Anchor = Anchor.TopLeft}, + textColor: Color.DarkGray * 0.6f, + text: TextManager.Get("Search") + "...", + textAlignment: Alignment.CenterLeft) + { + CanBeFocused = false + }; + searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; + searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = searchBox.Text.IsNullOrWhiteSpace(); }; + + searchBox.OnTextChanged += (sender, str) => + { + UpdateModListItemVisibility(); + return true; + }; + return searchBox; + } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs index 1a3e8f53c..ebe59aaf8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs @@ -4,9 +4,8 @@ namespace Barotrauma.Steam { abstract partial class WorkshopMenu { - public WorkshopMenu(GUIFrame parent) - { - - } + public WorkshopMenu(GUIFrame parent) { } + + protected abstract void UpdateModListItemVisibility(); } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 92e9d579c..fceac628c 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 9f9f372a2..cc6dc075d 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 8ffe5ccb4..77da65846 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index ab23d88c7..0abfeb8ce 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 3e74a9d79..3c9b6adea 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index c1a0eff87..7638769f0 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -1116,7 +1116,7 @@ namespace Barotrauma } else { - GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", "", t.Character.Name), Identifier.Empty, TraitorMessageType.Console); + GameMain.Server.SendTraitorMessage(client, string.Format("- Traitor {0} has no current objective.", t.Character.Name), Identifier.Empty, TraitorMessageType.Console); } } //GameMain.Server.SendTraitorMessage(client, "The code words are: " + traitorManager.CodeWords + ", response: " + traitorManager.CodeResponse + ".", TraitorMessageType.Console); @@ -2234,11 +2234,6 @@ namespace Barotrauma if (args.Length < 2) { GameMain.Server.SendConsoleMessage("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks.", senderClient, Color.Red); - return; - } - - if (args.Length < 2) - { ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] [character]\". If the names consist of multiple words, you should surround them with quotation marks."); return; } diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs index 484ebf03d..d04255896 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/CargoManager.cs @@ -7,7 +7,7 @@ namespace Barotrauma { partial class CargoManager { - public void SellBackPurchasedItems(Identifier storeIdentifier, List itemsToSell, Client client = null) + public void SellBackPurchasedItems(Identifier storeIdentifier, List itemsToSell, Client client) { // Check all the prices before starting the transaction to make sure the modifiers stay the same for the whole transaction var buyValues = GetBuyValuesAtCurrentLocation(storeIdentifier, itemsToSell.Select(i => i.ItemPrefab)); @@ -40,7 +40,7 @@ namespace Barotrauma } } - public void SellItems(Identifier storeIdentifier, List itemsToSell, Client client) + public void SellItems(Identifier storeIdentifier, List itemsToSell) { var store = Location.GetStore(storeIdentifier); if (store == null) { return; } diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index d509c9ae9..cef019215 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -564,6 +564,21 @@ namespace Barotrauma msg.Write((byte)selectedMissionIndex); } + var subList = GameMain.NetLobbyScreen.GetSubList(); + List ownedSubmarineIndices = new List(); + for (int i = 0; i < subList.Count; i++) + { + if (GameMain.GameSession.OwnedSubmarines.Any(s => s.Name == subList[i].Name)) + { + ownedSubmarineIndices.Add(i); + } + } + msg.Write((ushort)ownedSubmarineIndices.Count); + foreach (int index in ownedSubmarineIndices) + { + msg.Write((ushort)index); + } + msg.Write(map.AllowDebugTeleport); msg.Write(reputation != null); if (reputation != null) { msg.Write(reputation.Value); } @@ -768,8 +783,12 @@ namespace Barotrauma if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); } CheckTooManyMissions(Map.CurrentLocation, sender); } - - var prevBuyCrateItems = new Dictionary>(CargoManager.ItemsInBuyCrate); + + var prevBuyCrateItems = new Dictionary>(); + foreach (var kvp in CargoManager.ItemsInBuyCrate) + { + prevBuyCrateItems.Add(kvp.Key, new List(kvp.Value)); + } foreach (var store in prevBuyCrateItems) { foreach (var item in store.Value) @@ -784,10 +803,15 @@ namespace Barotrauma CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender); } } - var prevPurchasedItems = new Dictionary>(CargoManager.PurchasedItems); + + var prevPurchasedItems = new Dictionary>(); + foreach (var kvp in CargoManager.PurchasedItems) + { + prevPurchasedItems.Add(kvp.Key, new List(kvp.Value)); + } foreach (var store in prevPurchasedItems) { - CargoManager.SellBackPurchasedItems(store.Key, store.Value); + CargoManager.SellBackPurchasedItems(store.Key, store.Value, sender); } foreach (var store in purchasedItems) { @@ -813,6 +837,7 @@ namespace Barotrauma } } } + bool allowedToSellInventoryItems = AllowedToManageCampaign(sender, ClientPermissions.SellInventoryItems); if (allowedToSellInventoryItems && allowedToSellSubItems) { @@ -825,7 +850,7 @@ namespace Barotrauma } foreach (var store in soldItems) { - CargoManager.SellItems(store.Key, store.Value, sender); + CargoManager.SellItems(store.Key, store.Value); } } else if (allowedToSellInventoryItems || allowedToSellSubItems) @@ -842,7 +867,7 @@ namespace Barotrauma } foreach (var store in soldItems) { - CargoManager.SellItems(store.Key, store.Value, sender); + CargoManager.SellItems(store.Key, store.Value); } bool predicate(SoldItem i) => allowedToSellInventoryItems != (i.Origin == SoldItem.SellOrigin.Character); } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/Fabricator.cs index 8ffcf9742..347ef4ed5 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Machines/Fabricator.cs @@ -1,9 +1,6 @@ using Barotrauma.Networking; -using Microsoft.Xna.Framework; using System; -using System.Collections.Generic; using System.Linq; -using System.Xml.Linq; namespace Barotrauma.Items.Components { @@ -15,7 +12,7 @@ namespace Barotrauma.Items.Components item.CreateServerEvent(this); - if (!item.CanClientAccess(c)) return; + if (!item.CanClientAccess(c)) { return; } if (recipeHash == 0) { @@ -64,6 +61,13 @@ namespace Barotrauma.Items.Components msg.Write(recipeHash); UInt16 userID = fabricatedItem is null || user is null ? (UInt16)0 : user.ID; msg.Write(userID); + + var reachedLimits = fabricationLimits.Where(kvp => kvp.Value <= 0); + msg.Write((ushort)reachedLimits.Count()); + foreach (var kvp in reachedLimits) + { + msg.Write(kvp.Key); + } } } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs index ad2438c2d..c324baf78 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs @@ -38,7 +38,7 @@ namespace Barotrauma.Networking public bool CompareTo(string endpointCompare) { - if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(EndPoint)) { return false; } + if (string.IsNullOrEmpty(EndPoint) || string.IsNullOrEmpty(endpointCompare)) { return false; } if (!IsRangeBan) { return endpointCompare == EndPoint; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs index 4c1b87008..408c3ae61 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/FileSender.cs @@ -173,13 +173,14 @@ namespace Barotrauma.Networking } OnStarted(transfer); + GameMain.Server.LastClientListUpdateID++; return transfer; } public void Update(float deltaTime) { - activeTransfers.RemoveAll(t => t.Connection.Status != NetworkConnectionStatus.Connected); + int numRemoved = activeTransfers.RemoveAll(t => t.Connection.Status != NetworkConnectionStatus.Connected); var endedTransfers = activeTransfers.FindAll(t => t.Connection.Status != NetworkConnectionStatus.Connected || @@ -202,6 +203,11 @@ namespace Barotrauma.Networking Send(transfer); } } + + if (numRemoved > 0 || endedTransfers.Count > 0) + { + GameMain.Server.LastClientListUpdateID++; + } } private void Send(FileTransferOut transfer) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/ModSender.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/ModSender.cs index 93a36099e..f48d85f4d 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/ModSender.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/FileTransfer/ModSender.cs @@ -31,7 +31,7 @@ namespace Barotrauma.Networking string resultFileName = dir.StartsWith(ContentPackage.LocalModsDir) ? $"Local_{mod.Name}" - : $"Workshop_{mod.Name}"; + : $"Workshop_{mod.Name}_{mod.SteamWorkshopId}"; resultFileName = ToolBox.RemoveInvalidFileNameChars(resultFileName.Replace('\\', '_').Replace('/', '_')); resultFileName = $"{resultFileName}{Extension}"; return Path.Combine(UploadFolder, resultFileName); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index 0e2b53f6e..ce82de0fc 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -1587,25 +1587,6 @@ namespace Barotrauma.Networking outmsg.Write(serverSettings.AllowSpectating); c.WritePermissions(outmsg); - - if (gameStarted) - { - string ownedSubmarineIndexes = string.Empty; - for (int i = 0; i < subList.Count; i++) - { - if (GameMain.GameSession.OwnedSubmarines.Contains(subList[i])) - { - ownedSubmarineIndexes += i.ToString(); - ownedSubmarineIndexes += ";"; - } - } - - if (ownedSubmarineIndexes.Length > 0) - { - ownedSubmarineIndexes.Trim(';'); - } - outmsg.Write(ownedSubmarineIndexes); - } } private void ClientWriteIngame(Client c) @@ -1807,29 +1788,30 @@ namespace Barotrauma.Networking outmsg.Write((byte)connectedClients.Count); foreach (Client client in connectedClients) { - outmsg.Write(client.ID); - outmsg.Write(client.SteamID); - outmsg.Write(client.NameID); - outmsg.Write(client.Name); - outmsg.Write(client.Character?.Info?.Job != null && gameStarted ? client.Character.Info.Job.Prefab.Identifier : client.PreferredJob); - outmsg.Write((byte)client.PreferredTeam); - outmsg.Write(client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID); - if (c.HasPermission(ClientPermissions.ServerLog)) + var tempClientData = new TempClient { - outmsg.Write(client.Karma); - } - else - { - outmsg.Write(100.0f); - } - outmsg.Write(client.Muted); - outmsg.Write(client.InGame); - outmsg.Write(client.Permissions != ClientPermissions.None); - outmsg.Write(client.Connection == OwnerConnection); - outmsg.Write(client.Connection != OwnerConnection && - !client.HasPermission(ClientPermissions.Ban) && - !client.HasPermission(ClientPermissions.Kick) && - !client.HasPermission(ClientPermissions.Unban)); //is kicking the player allowed + ID = client.ID, + SteamID = client.SteamID, + NameID = client.NameID, + Name = client.Name, + PreferredJob = client.Character?.Info?.Job != null && gameStarted + ? client.Character.Info.Job.Prefab.Identifier + : client.PreferredJob, + PreferredTeam = client.PreferredTeam, + CharacterID = client.Character == null || !gameStarted ? (ushort)0 : client.Character.ID, + Karma = c.HasPermission(ClientPermissions.ServerLog) ? client.Karma : 100.0f, + Muted = client.Muted, + InGame = client.InGame, + HasPermissions = client.Permissions != ClientPermissions.None, + IsOwner = client.Connection == OwnerConnection, + AllowKicking = client.Connection != OwnerConnection && + !client.HasPermission(ClientPermissions.Ban) && + !client.HasPermission(ClientPermissions.Kick) && + !client.HasPermission(ClientPermissions.Unban), + IsDownloading = FileSender.ActiveTransfers.Any(t => t.Connection == client.Connection) + }; + + outmsg.Write(tempClientData); outmsg.WritePadBits(); } } @@ -3218,7 +3200,7 @@ namespace Barotrauma.Networking { Voting.ActiveVote.Finish(Voting, passed: false); } - else if (yes / max >= serverSettings.VoteRequiredRatio) + else if (yes / (float)max >= serverSettings.VoteRequiredRatio) { Voting.ActiveVote.Finish(Voting, passed: true); } diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 86864cc3c..a5861e794 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.17.5.0 + 0.17.6.0 Copyright © FakeFish 2018-2022 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs index e01c32de2..0edb7fa1d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs @@ -3450,7 +3450,7 @@ namespace Barotrauma { ChangeParams("wall", state, priority / 2); } - if (canAttackDoors) + if (canAttackDoors && IsAggressiveBoarder) { ChangeParams("door", state, priority / 2); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs index 83bcb0c42..4abcd54ee 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs @@ -376,19 +376,17 @@ namespace Barotrauma { Vector2 diff = currentPath.CurrentNode.WorldPosition - pos; bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent; - if (nextLadderSameAsCurrent) + if (nextLadderSameAsCurrent || currentLadder != null && nextLadder != null && Math.Abs(currentLadder.Item.Position.X - nextLadder.Item.Position.X) < 50) { //climbing ladders -> don't move horizontally diff.X = 0.0f; } //at the same height as the waypoint - if (Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < (collider.height / 2 + collider.radius) * 1.25f) + float heightDiff = Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y); + float colliderSize = (collider.height / 2 + collider.radius) * 1.25f; + if (heightDiff < colliderSize) { float heightFromFloor = character.AnimController.GetHeightFromFloor(); - if (heightFromFloor <= 0.0f) - { - diff.Y = Math.Max(diff.Y, 100); - } // We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative. bool isAboveFloor = heightFromFloor > -0.1f; // If the next waypoint is horizontally far, we don't want to keep holding the ladders @@ -402,7 +400,10 @@ namespace Barotrauma // Try to change the ladder (hatches between two submarines) if (character.SelectedConstruction != nextLadder.Item && nextLadder.Item.IsInsideTrigger(character.WorldPosition)) { - nextLadder.Item.TryInteract(character, forceSelectKey: true); + if (nextLadder.Item.TryInteract(character, forceSelectKey: true)) + { + NextNode(!doorsChecked); + } } } if (isAboveFloor || nextLadderSameAsCurrent) @@ -461,12 +462,16 @@ namespace Barotrauma bool isTargetTooLow = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y; var door = currentPath.CurrentNode.ConnectedDoor; float margin = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 5, 0, 1)); - if (currentPath.CurrentNode.Stairs != null && currentPath.NextNode?.Stairs == null) + if (currentPath.CurrentNode.Stairs != null) { - margin = 1; - if (currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + character.AnimController.ColliderHeightFromFloor * 0.25f) + bool isNextNodeInSameStairs = currentPath.NextNode?.Stairs == currentPath.CurrentNode.Stairs; + if (!isNextNodeInSameStairs) { - isTargetTooLow = true; + margin = 1; + if (currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + character.AnimController.ColliderHeightFromFloor * 0.25f) + { + isTargetTooLow = true; + } } } float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs index 9342c4239..9b05c7e0a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs @@ -630,7 +630,7 @@ namespace Barotrauma public bool IsActiveObjective() where T : AIObjective => GetActiveObjective() is T; public AIObjective GetActiveObjective() => CurrentObjective?.GetActiveObjective(); - public T GetOrder() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T).Objective as T; + public T GetOrder() where T : AIObjective => CurrentOrders.FirstOrDefault(o => o.Objective is T)?.Objective as T; /// /// Returns the last active objective of the specific type. diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs index e282061e2..c66746f9a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs @@ -24,6 +24,8 @@ namespace Barotrauma public bool IsAiming => wasAiming; public bool IsAimingMelee => wasAimingMelee; + protected bool Aiming => aiming || aimingMelee; + public float ArmLength => upperArmLength + forearmLength; public abstract GroundedMovementParams WalkParams { get; set; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/FishAnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/FishAnimController.cs index f898ac8d4..81b328b03 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/FishAnimController.cs @@ -193,7 +193,7 @@ namespace Barotrauma strongestImpact = 0.0f; } - if (aiming) + if (Aiming) { TargetMovement = TargetMovement.ClampLength(2); } @@ -233,7 +233,7 @@ namespace Barotrauma //don't flip when simply physics is enabled if (SimplePhysicsEnabled) { return; } - if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip) && !aiming) + if (!character.IsRemotelyControlled && (character.AIController == null || character.AIController.CanFlip) && !Aiming) { if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror)) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs index 30fc791e6..7953deff1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/HumanoidAnimController.cs @@ -597,11 +597,11 @@ namespace Barotrauma { float torsoAngle = TorsoAngle.Value; float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes"); - if (Crouching && !movingHorizontally && !aiming) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; } + if (Crouching && !movingHorizontally && !Aiming) { torsoAngle -= HumanCrouchParams.ExtraTorsoAngleWhenStationary; } torsoAngle -= herpesStrength / 150.0f; torso.body.SmoothRotate(torsoAngle * Dir, CurrentGroundedParams.TorsoTorque); } - if (!aiming && CurrentGroundedParams.FixedHeadAngle && HeadAngle.HasValue) + if (!Aiming && CurrentGroundedParams.FixedHeadAngle && HeadAngle.HasValue) { float headAngle = HeadAngle.Value; if (Crouching && !movingHorizontally) { headAngle -= HumanCrouchParams.ExtraHeadAngleWhenStationary; } @@ -817,48 +817,16 @@ namespace Barotrauma Limb torso = GetLimb(LimbType.Torso); if (head == null) { return; } if (torso == null) { return; } - - //check both hulls: the hull whose coordinate space the ragdoll is in, and the hull whose bounds the character's origin actually is inside + + const float DisableMovementAboveSurfaceThreshold = 50.0f; + if (currentHull != null && character.CurrentHull != null) { - float surfacePos = currentHull.Surface; + float surfacePos = GetSurfaceY(); float surfaceThreshold = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f); - //if the hull is almost full of water, check if there's a water-filled hull above it - //and use its water surface instead of the current hull's - if (currentHull.Rect.Y - currentHull.Surface < 5.0f) - { - GetSurfacePos(currentHull, ref surfacePos); - void GetSurfacePos(Hull hull, ref float prevSurfacePos) - { - if (prevSurfacePos > surfaceThreshold) { return; } - foreach (Gap gap in hull.ConnectedGaps) - { - if (gap.IsHorizontal || gap.Open <= 0.0f || gap.WorldPosition.Y < hull.WorldPosition.Y) { continue; } - if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; } - - //if the gap is above us and leads outside, there's no surface to limit the movement - if (!gap.IsRoomToRoom && gap.Position.Y > hull.Position.Y) - { - prevSurfacePos += 100000.0f; - return; - } - - foreach (var linkedTo in gap.linkedTo) - { - if (linkedTo is Hull otherHull && otherHull != hull && otherHull != currentHull) - { - prevSurfacePos = Math.Max(surfacePos, otherHull.Surface); - GetSurfacePos(otherHull, ref prevSurfacePos); - break; - } - } - } - } - } - surfaceLimiter = Math.Max(1.0f, surfaceThreshold - surfacePos); - if (surfaceLimiter > 50.0f) { return; } - } + if (surfaceLimiter > DisableMovementAboveSurfaceThreshold) { return; } + } Limb leftHand = GetLimb(LimbType.LeftHand); Limb rightHand = GetLimb(LimbType.RightHand); @@ -872,25 +840,30 @@ namespace Barotrauma { rotation += 360; } - if (!character.IsRemotelyControlled && !aiming && Anim != Animation.UsingConstruction && - !(character.SelectedConstruction?.GetComponent()?.ControlCharacterPose ?? false)) + float targetSpeed = TargetMovement.Length(); + if (targetSpeed > 0.1f && !character.IsRemotelyControlled && !character.IsKeyDown(InputType.Aim)) { - if (rotation > 20 && rotation < 170) + if (Anim != Animation.UsingConstruction && !(character.SelectedConstruction?.GetComponent()?.ControlCharacterPose ?? false)) { - TargetDir = Direction.Left; - } - else if (rotation > 190 && rotation < 340) - { - TargetDir = Direction.Right; + if (rotation > 20 && rotation < 170) + { + TargetDir = Direction.Left; + } + else if (rotation > 190 && rotation < 340) + { + TargetDir = Direction.Right; + } } } - float targetSpeed = TargetMovement.Length(); - if (aiming) + if (Aiming) { Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition); Vector2 diff = (mousePos - torso.SimPosition) * Dir; - float newRotation = MathUtils.VectorToAngle(diff); - Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier); + if (diff.LengthSquared() > MathUtils.Pow2(0.4f)) + { + float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 * Dir); + Collider.SmoothRotate(newRotation, CurrentSwimParams.SteerTorque * character.SpeedMultiplier); + } } else if (targetSpeed > 0.1f) { @@ -911,7 +884,7 @@ namespace Barotrauma torso.body.SmoothRotate(Collider.Rotation, CurrentSwimParams.TorsoTorque); } - if (!aiming && CurrentSwimParams.FixedHeadAngle && HeadAngle.HasValue) + if (!Aiming && CurrentSwimParams.FixedHeadAngle && HeadAngle.HasValue) { head.body.SmoothRotate(Collider.Rotation + HeadAngle.Value * Dir, CurrentSwimParams.HeadTorque); } @@ -940,7 +913,7 @@ namespace Barotrauma head.body.ApplyTorque(Dir); } - movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / 50.0f)); + movement.Y = movement.Y * (1.0f - ((surfaceLimiter - 1.0f) / DisableMovementAboveSurfaceThreshold)); } bool isNotRemote = true; @@ -1141,10 +1114,9 @@ namespace Barotrauma bottomPos + torsoPos + movement.Y * 0.1f - ladderSimPos.Y); if (climbFast) { handPos.Y -= stepHeight; } - bool aiming = this.aiming || aimingMelee; //prevent the hands from going above the top of the ladders handPos.Y = Math.Min(-0.5f, handPos.Y); - if (!aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f) + if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f) { MoveLimb(rightHand, new Vector2(slide ? handPos.X + ladderSimSize.X * 0.5f : handPos.X, @@ -1152,7 +1124,7 @@ namespace Barotrauma 5.2f); rightHand.body.ApplyTorque(Dir * 2.0f); } - if (!aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f) + if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f) { MoveLimb(leftHand, new Vector2(handPos.X - ladderSimSize.X * 0.5f, @@ -1235,7 +1207,7 @@ namespace Barotrauma //apply forces to the collider to move the Character up/down Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass); - if (aiming) + if (Aiming) { RotateHead(head); } @@ -1526,11 +1498,14 @@ namespace Barotrauma return; } Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso); - if (targetTorso == null) targetTorso = target.AnimController.MainLimb; - + if (targetTorso == null) + { + targetTorso = target.AnimController.MainLimb; + } if (target.AnimController.Dir != Dir) + { target.AnimController.Flip(); - + } Vector2 transformedTorsoPos = torso.SimPosition; if (character.Submarine == null && target.Submarine != null) { @@ -1574,7 +1549,10 @@ namespace Barotrauma { //only grab with one hand when swimming leftHand.Disabled = true; - if (!inWater) rightHand.Disabled = true; + if (!inWater) + { + rightHand.Disabled = true; + } for (int i = 0; i < 2; i++) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs index 9277a79ee..294285a87 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs @@ -1193,13 +1193,9 @@ namespace Barotrauma headInWater = false; inWater = false; RefreshFloorY(ignoreStairs: Stairs == null); - if (currentHull.WaterVolume > currentHull.Volume * 0.95f) + if (currentHull.WaterPercentage > 0.001f) { - inWater = true; - } - else - { - float waterSurface = ConvertUnits.ToSimUnits(currentHull.Surface); + float waterSurface = ConvertUnits.ToSimUnits(GetSurfaceY()); if (targetMovement.Y < 0.0f) { Vector2 colliderBottom = GetColliderBottom(); @@ -1212,11 +1208,8 @@ namespace Barotrauma if (lowerHull != null) floorY = ConvertUnits.ToSimUnits(lowerHull.Rect.Y - lowerHull.Rect.Height); } } - float standHeight = - HeadPosition.HasValue ? HeadPosition.Value : - TorsoPosition.HasValue ? TorsoPosition.Value : - Collider.GetMaxExtent() * 0.5f; - if (Collider.SimPosition.Y < waterSurface && waterSurface - floorY > standHeight * 0.95f) + float standHeight = HeadPosition ?? TorsoPosition ?? Collider.GetMaxExtent() * 0.5f; + if (Collider.SimPosition.Y < waterSurface && waterSurface - floorY > standHeight * 0.8f) { inWater = true; } @@ -1521,7 +1514,6 @@ namespace Barotrauma } } - private float GetFloorY(Vector2 simPosition, bool ignoreStairs = false) { onGround = false; @@ -1640,6 +1632,51 @@ namespace Barotrauma } } + public float GetSurfaceY() + { + //check both hulls: the hull whose coordinate space the ragdoll is in, and the hull whose bounds the character's origin actually is inside + if (currentHull == null || character.CurrentHull == null) + { + return float.PositiveInfinity; + } + + float surfacePos = currentHull.Surface; + float surfaceThreshold = ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 1.0f); + //if the hull is almost full of water, check if there's a water-filled hull above it + //and use its water surface instead of the current hull's + if (currentHull.Rect.Y - currentHull.Surface < 5.0f) + { + GetSurfacePos(currentHull, ref surfacePos); + void GetSurfacePos(Hull hull, ref float prevSurfacePos) + { + if (prevSurfacePos > surfaceThreshold) { return; } + foreach (Gap gap in hull.ConnectedGaps) + { + if (gap.IsHorizontal || gap.Open <= 0.0f || gap.WorldPosition.Y < hull.WorldPosition.Y) { continue; } + if (Collider.SimPosition.X < ConvertUnits.ToSimUnits(gap.Rect.X) || Collider.SimPosition.X > ConvertUnits.ToSimUnits(gap.Rect.Right)) { continue; } + + //if the gap is above us and leads outside, there's no surface to limit the movement + if (!gap.IsRoomToRoom && gap.Position.Y > hull.Position.Y) + { + prevSurfacePos += 100000.0f; + return; + } + + foreach (var linkedTo in gap.linkedTo) + { + if (linkedTo is Hull otherHull && otherHull != hull && otherHull != currentHull) + { + prevSurfacePos = Math.Max(surfacePos, otherHull.Surface); + GetSurfacePos(otherHull, ref prevSurfacePos); + break; + } + } + } + } + } + return surfacePos; + } + public void SetPosition(Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true, bool forceMainLimbToCollider = false, bool detachProjectiles = true) { if (!MathUtils.IsValid(simPosition)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs index e405a887e..e524f3a87 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs @@ -349,7 +349,6 @@ namespace Barotrauma DamageRange = range; StructureDamage = LevelWallDamage = structureDamage; ItemDamage = itemDamage; - Penetration = Penetration; } public Attack(ContentXElement element, string parentDebugName, Item sourceItem) : this(element, parentDebugName) @@ -359,7 +358,7 @@ namespace Barotrauma public Attack(ContentXElement element, string parentDebugName) { - Deserialize(element); + Deserialize(element, parentDebugName); if (element.GetAttribute("damage") != null || element.GetAttribute("bluntdamage") != null || @@ -423,7 +422,7 @@ namespace Barotrauma } partial void InitProjSpecific(ContentXElement element); - public void ReloadAfflictions(XElement element) + public void ReloadAfflictions(XElement element, string parentDebugName) { Afflictions.Clear(); foreach (var subElement in element.GetChildElements("affliction")) @@ -431,6 +430,11 @@ namespace Barotrauma AfflictionPrefab afflictionPrefab; Affliction affliction; Identifier afflictionIdentifier = subElement.GetAttributeIdentifier("identifier", ""); + if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionIdentifier)) + { + DebugConsole.ThrowError($"Error in an Attack defined in \"{parentDebugName}\" - could not find an affliction with the identifier \"{afflictionIdentifier}\"."); + continue; + } afflictionPrefab = AfflictionPrefab.Prefabs[afflictionIdentifier]; affliction = afflictionPrefab.Instantiate(0.0f); affliction.Deserialize(subElement); @@ -456,10 +460,10 @@ namespace Barotrauma } } - public void Deserialize(XElement element) + public void Deserialize(XElement element, string parentDebugName) { SerializableProperties = SerializableProperty.DeserializeProperties(this, element); - ReloadAfflictions(element); + ReloadAfflictions(element, parentDebugName); } public AttackResult DoDamage(Character attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true, PhysicsBody sourceBody = null, Limb sourceLimb = null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index 4d307d4c7..5182ff0b6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -556,6 +556,12 @@ namespace Barotrauma #if CLIENT CharacterHealth.SetHealthBarVisibility(value == null); +#elif SERVER + if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet }) + { + Wallet.Give(balance); + grabbedWallet.Deduct(balance); + } #endif } } @@ -1180,7 +1186,7 @@ namespace Barotrauma CharacterHealth = new CharacterHealth(selectedHealthElement, this, limbHealthElement); } - if (Params.Husk) + if (Params.Husk && speciesName != "husk") { // Get the non husked name and find the ragdoll with it var matchingAffliction = AfflictionPrefab.List @@ -1764,26 +1770,44 @@ namespace Barotrauma } if (!aiControlled && - AnimController.OnGround && - !AnimController.InWater && AnimController.Anim != AnimController.Animation.UsingConstruction && AnimController.Anim != AnimController.Animation.CPR && - (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient || Controlled == this)) + (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient || Controlled == this) && + (AnimController.OnGround && !AnimController.InWater || IsKeyDown(InputType.Aim) && HeldItems.None(i => i.RequireAimToUse))) { - //Limb head = AnimController.GetLimb(LimbType.Head); - // Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping. - float followMargin = 40; if (dontFollowCursor) { AnimController.TargetDir = Direction.Right; } - else if (cursorPosition.X < AnimController.Collider.Position.X - followMargin) + else { - AnimController.TargetDir = Direction.Left; - } - else if (cursorPosition.X > AnimController.Collider.Position.X + followMargin) - { - AnimController.TargetDir = Direction.Right; + // Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping. + float followMargin = 40; + Vector2 diff = CursorPosition - AnimController.Collider.Position; + if (InWater) + { + followMargin = 80; + diff = Vector2.Transform(diff, Matrix.CreateRotationZ(-AnimController.Collider.Rotation)); + if (diff.X < followMargin) + { + AnimController.TargetDir = Direction.Left; + } + else if (diff.X > followMargin) + { + AnimController.TargetDir = Direction.Right; + } + } + else + { + if (CursorPosition.X < AnimController.Collider.Position.X - followMargin) + { + AnimController.TargetDir = Direction.Left; + } + else if (CursorPosition.X > AnimController.Collider.Position.X + followMargin) + { + AnimController.TargetDir = Direction.Right; + } + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs index 86957c713..6952cd722 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs @@ -53,16 +53,18 @@ namespace Barotrauma continue; } var vitalityMultipliers = subElement.GetAttributeIdentifierArray("identifier", null) ?? subElement.GetAttributeIdentifierArray("identifiers", null); - if (vitalityMultipliers == null) - { - vitalityMultipliers = subElement.GetAttributeIdentifierArray("type", null) ?? subElement.GetAttributeIdentifierArray("types", null); - } if (vitalityMultipliers != null) { float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f); vitalityMultipliers.ForEach(i => VitalityMultipliers.Add(i, multiplier)); } - else + var vitalityTypeMultipliers = subElement.GetAttributeIdentifierArray("type", null) ?? subElement.GetAttributeIdentifierArray("types", null); + if (vitalityTypeMultipliers != null) + { + float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f); + vitalityTypeMultipliers.ForEach(i => VitalityTypeMultipliers.Add(i, multiplier)); + } + if (vitalityMultipliers == null && VitalityTypeMultipliers == null) { DebugConsole.ThrowError($"Error in character health config {characterHealth.Character.Name}: affliction identifier(s) or type(s) not defined in the \"VitalityMultiplier\" elements!"); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs index 7db320395..608d7f16f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Ragdoll/RagdollParams.cs @@ -1116,14 +1116,13 @@ namespace Barotrauma public AttackParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll) { - var prefab = CharacterPrefab.Prefabs[ragdoll.SpeciesName]; Attack = new Attack(element, ragdoll.SpeciesName.Value); } public override bool Deserialize(XElement element = null, bool recursive = true) { base.Deserialize(element, recursive); - Attack.Deserialize(element ?? Element); + Attack.Deserialize(element ?? Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null"); return SerializableProperties != null; } @@ -1137,8 +1136,8 @@ namespace Barotrauma public override void Reset() { base.Reset(); - Attack.Deserialize(OriginalElement); - Attack.ReloadAfflictions(OriginalElement); + Attack.Deserialize(OriginalElement, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null"); + Attack.ReloadAfflictions(OriginalElement, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null"); } public bool AddNewAffliction() @@ -1149,7 +1148,7 @@ namespace Barotrauma new XAttribute("strength", 0f), new XAttribute("probability", 1.0f)); Element.Add(subElement); - Attack.ReloadAfflictions(Element); + Attack.ReloadAfflictions(Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null"); Serialize(); return true; } @@ -1158,7 +1157,7 @@ namespace Barotrauma { Serialize(); affliction.Remove(); - Attack.ReloadAfflictions(Element); + Attack.ReloadAfflictions(Element, parentDebugName: Ragdoll?.SpeciesName.ToString() ?? "null"); return Serialize(); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ItemAssemblyFile.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ItemAssemblyFile.cs index 71f5cd664..bff61b1bb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ItemAssemblyFile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/ItemAssemblyFile.cs @@ -2,6 +2,7 @@ using System.Xml.Linq; namespace Barotrauma { + [NotSyncedInMultiplayer] sealed class ItemAssemblyFile : GenericPrefabFile { public ItemAssemblyFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { } diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs index de37b623a..9f21480f9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentFile/TextFile.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Xml.Linq; diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs index 70f3ebdaf..add517a0c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs @@ -156,6 +156,19 @@ namespace Barotrauma return -1; } + public static void DisableMods(IReadOnlyCollection mods) + { + if (Core != null && mods.Contains(Core)) + { + var newCore = ContentPackageManager.CorePackages.FirstOrDefault(p => !mods.Contains(p)); + if (newCore != null) + { + SetCore(newCore); + } + } + SetRegular(Regular.Where(p => !mods.Contains(p)).ToArray()); + } + public static void DisableRemovedMods() { if (Core != null && !ContentPackageManager.CorePackages.Contains(Core)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs index 96bb78082..150aee8b6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs @@ -40,22 +40,25 @@ namespace Barotrauma public readonly static PrefabCollection Prefabs = new PrefabCollection(); #if CLIENT - private static readonly Dictionary EventSprites = new Dictionary(); - public static Sprite GetEventSprite(string identifier) { if (string.IsNullOrWhiteSpace(identifier)) { return null; } - foreach (var (key, value) in EventSprites) + if (EventSprite.Prefabs.TryGet(identifier.ToIdentifier(), out EventSprite sprite)) { - if (key.Equals(identifier, StringComparison.OrdinalIgnoreCase)) { return value; } + return sprite.Sprite; } +#if DEBUG || UNSTABLE + DebugConsole.ThrowError($"Could not find the event sprite \"{identifier}\""); +#else + DebugConsole.AddWarning($"Could not find the event sprite \"{identifier}\""); +#endif return null; } #endif - public static List GetAllEventPrefabs() + public static List GetAllEventPrefabs() { List eventPrefabs = EventPrefab.Prefabs.ToList(); foreach (var eventSet in Prefabs) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs index d7d042774..11ed0fd52 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/BeaconMission.cs @@ -172,7 +172,7 @@ namespace Barotrauma ChangeLocationType(Prefab.LocationTypeChangeOnCompleted); } GiveReward(); - if (level?.LevelData != null) + if (level.LevelData != null) { level.LevelData.IsBeaconActive = true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index ac1b2e40b..44a2cdf9b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -95,23 +95,10 @@ namespace Barotrauma partial void InitProjSpecific(); - private GameSession(SubmarineInfo submarineInfo, List? ownedSubmarines = null) + private GameSession(SubmarineInfo submarineInfo) { InitProjSpecific(); SubmarineInfo = submarineInfo; - -#if CLIENT - if (ownedSubmarines == null && GameMode is MultiPlayerCampaign && GameMain.NetLobbyScreen.ServerOwnedSubmarines != null) - { - ownedSubmarines = GameMain.NetLobbyScreen.ServerOwnedSubmarines; - } -#endif - - OwnedSubmarines = ownedSubmarines ?? new List(); - if (!OwnedSubmarines.Any(s => s.Name == submarineInfo.Name)) - { - OwnedSubmarines.Add(submarineInfo); - } GameMain.GameSession = this; EventManager = new EventManager(); } @@ -125,6 +112,7 @@ namespace Barotrauma this.SavePath = savePath; CrewManager = new CrewManager(gameModePreset.IsSinglePlayer); GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, settings, missionType: missionType); + InitOwnedSubs(submarineInfo); } /// @@ -135,12 +123,13 @@ namespace Barotrauma { CrewManager = new CrewManager(gameModePreset.IsSinglePlayer); GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, CampaignSettings.Empty, missionPrefabs: missionPrefabs); + InitOwnedSubs(submarineInfo); } /// /// Load a game session from the specified XML document. The session will be saved to the specified path. /// - public GameSession(SubmarineInfo submarineInfo, List ownedSubmarines, XDocument doc, string saveFile) : this(submarineInfo, ownedSubmarines) + public GameSession(SubmarineInfo submarineInfo, List ownedSubmarines, XDocument doc, string saveFile) : this(submarineInfo) { this.SavePath = saveFile; GameMain.GameSession = this; @@ -173,6 +162,16 @@ namespace Barotrauma break; } } + InitOwnedSubs(submarineInfo); + } + + private void InitOwnedSubs(SubmarineInfo submarineInfo, List? ownedSubmarines = null) + { + OwnedSubmarines = ownedSubmarines ?? new List(); + if (submarineInfo != null && !OwnedSubmarines.Any(s => s.Name == submarineInfo.Name)) + { + OwnedSubmarines.Add(submarineInfo); + } } private GameMode InstantiateGameMode(GameModePreset gameModePreset, string? seed, SubmarineInfo selectedSub, CampaignSettings settings, IEnumerable? missionPrefabs = null, MissionType missionType = MissionType.None) @@ -295,7 +294,8 @@ namespace Barotrauma Campaign!.GetWallet(client).TryDeduct(cost); } GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name); - + Campaign!.PendingSubmarineSwitch = newSubmarine; + return newSubmarine; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs index ee10aee6e..4c6a29915 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs @@ -116,8 +116,6 @@ namespace Barotrauma.Items.Components this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary(); state = FabricatorState.Stopped; - - InitProjSpecific(); } public override void OnItemLoaded() @@ -146,9 +144,6 @@ namespace Barotrauma.Items.Components partial void OnItemLoadedProjSpecific(); - - partial void InitProjSpecific(); - public override bool Select(Character character) { SelectProjSpecific(character); @@ -192,7 +187,7 @@ namespace Barotrauma.Items.Components if (!isClient) { MoveIngredientsToInputContainer(selectedItem); - if (selectedItem.RequiredMoney > 0) + if (selectedItem.RequiredMoney > 0 && CanBeFabricated(fabricatedItem, availableIngredients, user)) { if (GameMain.GameSession?.GameMode is MultiPlayerCampaign) { @@ -395,14 +390,17 @@ namespace Barotrauma.Items.Components var fabricationitemAmount = new AbilityFabricationItemAmount(fabricatedItem.TargetItem, fabricatedItem.Amount); int quality = 0; - if (user?.Info != null) + if (fabricatedItem.Quality.HasValue) + { + quality = fabricatedItem.Quality.Value; + } + else if (user?.Info != null) { foreach (Character character in Character.GetFriendlyCrew(user)) { character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount); } - user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount); - + user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount); quality = GetFabricatedItemQuality(fabricatedItem, user); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index 411627eef..028e59a8e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -1196,7 +1196,7 @@ namespace Barotrauma drawableComponents.Add(drawable); hasComponentsToDraw = true; #if CLIENT - cachedVisibleSize = null; + cachedVisibleExtents = null; #endif } } @@ -1208,7 +1208,7 @@ namespace Barotrauma drawableComponents.Remove(drawable); hasComponentsToDraw = drawableComponents.Count > 0; #if CLIENT - cachedVisibleSize = null; + cachedVisibleExtents = null; #endif } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs index 8e25476cc..505837205 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs @@ -118,6 +118,7 @@ namespace Barotrauma public readonly ImmutableArray RequiredSkills; public readonly uint RecipeHash; public readonly int Amount; + public readonly int? Quality; /// /// How many of this item the fabricator can create (< 0 = unlimited) @@ -150,6 +151,11 @@ namespace Barotrauma FabricationLimitMin = element.GetAttributeInt(nameof(FabricationLimitMin), limitDefault); FabricationLimitMax = element.GetAttributeInt(nameof(FabricationLimitMax), limitDefault); + if (element.GetAttribute(nameof(Quality)) != null) + { + Quality = element.GetAttributeInt(nameof(Quality), 0); + } + foreach (var subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs index 05539c6e2..227f59f3d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/ItemAssemblyPrefab.cs @@ -15,7 +15,6 @@ namespace Barotrauma public static readonly PrefabCollection Prefabs = new PrefabCollection(); public static readonly string VanillaSaveFolder = Path.Combine("Content", "Items", "Assemblies"); - public static readonly string SaveFolder = "ItemAssemblies"; private readonly XElement configElement; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelObjectManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelObjectManager.cs index c3b30aac8..5fd728e3e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelObjectManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelObjectManager.cs @@ -282,7 +282,7 @@ namespace Barotrauma private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level, Level.Cave parentCave = null) { float rotation = 0.0f; - if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && spawnPosition != null) + if (prefab.AlignWithSurface && spawnPosition != null && spawnPosition.Normal.LengthSquared() > 0.001f) { rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X)); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs index 84d2510c2..f39a30ee4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs @@ -359,8 +359,8 @@ namespace Barotrauma /// How many map progress steps it takes before the discounts should be updated. /// private const int SpecialsUpdateInterval = 3; - private int DailySpecialsCount => Type.DailySpecialsCount; - private int RequestedGoodsCount => Type.RequestedGoodsCount; + public int DailySpecialsCount => Type.DailySpecialsCount; + public int RequestedGoodsCount => Type.RequestedGoodsCount; private int StepsSinceSpecialsUpdated { get; set; } public HashSet StoreIdentifiers { get; } = new HashSet(); @@ -1138,7 +1138,7 @@ namespace Barotrauma { store.Balance = Math.Min(store.Balance + (int)(StoreInitialBalance / 10.0f), StoreInitialBalance); } - var stock = store.Stock; + var stock = new List(store.Stock); var stockToRemove = new List(); foreach (var item in stock) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/PriceInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/PriceInfo.cs index 7e9901866..96a8e9028 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/PriceInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/PriceInfo.cs @@ -89,22 +89,18 @@ namespace Barotrauma { backwardsCompatibleIdentifier = $"merchant{backwardsCompatibleIdentifier}"; } - string[] storeIdentifiers = childElement.GetAttributeStringArray("storeidentifiers", new string[1] { backwardsCompatibleIdentifier }); - foreach (string id in storeIdentifiers) - { - if (string.IsNullOrEmpty(id)) { continue; } - // TODO: Add some error messages if we have defined the min or max amount while the item is not sold - var priceInfo = new PriceInfo((int)(priceMultiplier * basePrice), - sold, - sold ? GetMinAmount(childElement, minAmount) : 0, - sold ? GetMaxAmount(childElement, maxAmount) : 0, - canBeSpecial, - storeMinLevelDifficulty, - storeBuyingMultiplier, - displayNonEmpty, - id); - priceInfos.Add(priceInfo); - } + string storeIdentifier = childElement.GetAttributeString("storeidentifier", backwardsCompatibleIdentifier); + // TODO: Add some error messages if we have defined the min or max amount while the item is not sold + var priceInfo = new PriceInfo((int)(priceMultiplier * basePrice), + sold, + sold ? GetMinAmount(childElement, minAmount) : 0, + sold ? GetMaxAmount(childElement, maxAmount) : 0, + canBeSpecial, + storeMinLevelDifficulty, + storeBuyingMultiplier, + displayNonEmpty, + storeIdentifier); + priceInfos.Add(priceInfo); } bool soldElsewhere = soldByDefault && element.GetAttributeBool("soldelsewhere", element.GetAttributeBool("soldeverywhere", false)); defaultPrice = new PriceInfo(basePrice, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs index 11b32da05..23a7d0e1f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs @@ -313,7 +313,16 @@ namespace Barotrauma for (float x = hull.Rect.X + diffFromHullEdge; x <= hull.Rect.Right - diffFromHullEdge; x += minDist) { var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine); - if (previousWaypoint != null) { wayPoint.ConnectTo(previousWaypoint); } + // Too close to stairs, will be assigned as a stair point -> remove + if (wayPoint.FindStairs() != null) + { + removals.Add(wayPoint); + continue; + } + if (previousWaypoint != null) + { + wayPoint.ConnectTo(previousWaypoint); + } previousWaypoint = wayPoint; } if (previousWaypoint == null) @@ -510,25 +519,29 @@ namespace Barotrauma } } } + removals.ForEach(wp => wp.Remove()); + removals.Clear(); + // Stairs foreach (MapEntity mapEntity in mapEntityList.ToList()) { if (!(mapEntity is Structure structure)) { continue; } if (structure.StairDirection == Direction.None) { continue; } WayPoint[] stairPoints = new WayPoint[3]; + float margin = -32; - stairPoints[0] = new WayPoint( - new Vector2(structure.Rect.X - 32.0f, - structure.Rect.Y - (structure.StairDirection == Direction.Left ? 80 : structure.Rect.Height) + heightFromFloor), SpawnType.Path, submarine); + stairPoints[0] = new WayPoint(new Vector2( + structure.Rect.X + 5, + structure.Rect.Y - (structure.StairDirection == Direction.Left ? margin : structure.Rect.Height - 100)), SpawnType.Path, submarine); - stairPoints[1] = new WayPoint( - new Vector2(structure.Rect.Right + 32.0f, - structure.Rect.Y - (structure.StairDirection == Direction.Left ? structure.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine); + stairPoints[1] = new WayPoint(new Vector2( + structure.Rect.Right - 5, + structure.Rect.Y - (structure.StairDirection == Direction.Left ? structure.Rect.Height - 100 : margin)), SpawnType.Path, submarine); for (int i = 0; i < 2; i++) { for (int dir = -1; dir <= 1; dir += 2) { - WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(100, 70)); + WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(minDist * 1.5f, minDist / 2)); if (closest == null) { continue; } stairPoints[i].ConnectTo(closest); } @@ -537,9 +550,8 @@ namespace Barotrauma stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine); stairPoints[0].ConnectTo(stairPoints[2]); stairPoints[2].ConnectTo(stairPoints[1]); + stairPoints.ForEach(wp => wp.FindStairs()); } - removals.ForEach(wp => wp.Remove()); - removals.Clear(); foreach (Item item in Item.ItemList) { @@ -840,7 +852,11 @@ namespace Barotrauma var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false); if (body != null && body != ignoredBody && !(body.UserData is Submarine)) { - if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall)) + if (body.UserData is Structure) + { + continue; + } + if (body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall) && body.UserData is Item i && i.GetComponent() != null) { continue; } @@ -960,14 +976,15 @@ namespace Barotrauma FindStairs(); } - private void FindStairs() + private Structure FindStairs() { Stairs = null; - Body pickedBody = Submarine.PickBody(SimPosition, SimPosition - Vector2.UnitY * 2.0f, null, Physics.CollisionStairs); + Body pickedBody = Submarine.PickBody(SimPosition, SimPosition - new Vector2(0, 1.2f), null, Physics.CollisionStairs); if (pickedBody != null && pickedBody.UserData is Structure structure && structure.StairDirection != Direction.None) { - Stairs = structure; + Stairs = structure; } + return Stairs; } public void InitializeLinks() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs index daf4b37c4..b7edffbbb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs @@ -6,6 +6,25 @@ using System.Linq; namespace Barotrauma.Networking { + [NetworkSerialize] + struct TempClient : INetSerializableStruct + { + public string Name; + public Identifier PreferredJob; + public CharacterTeamType PreferredTeam; + public UInt16 NameID; + public UInt64 SteamID; + public byte ID; + public UInt16 CharacterID; + public float Karma; + public bool Muted; + public bool InGame; + public bool HasPermissions; + public bool IsOwner; + public bool AllowKicking; + public bool IsDownloading; + } + partial class Client : IDisposable { public const int MaxNameLength = 32; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs index 84690b56b..e7c0cecab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs @@ -58,10 +58,13 @@ namespace Barotrauma cam.Position = Submarine.MainSub.WorldPosition; cam.UpdateTransform(true); } + GameMain.GameSession?.CrewManager?.AutoShowCrewList(); #endif foreach (MapEntity entity in MapEntity.mapEntityList) + { entity.IsHighlighted = false; + } #if RUN_PHYSICS_IN_SEPARATE_THREAD var physicsThread = new Thread(ExecutePhysics) @@ -78,6 +81,10 @@ namespace Barotrauma base.Deselect(); #if CLIENT + var config = GameSettings.CurrentConfig; + config.CrewMenuOpen = CrewManager.PreferCrewMenuOpen; + config.ChatOpen = ChatBox.PreferChatBoxOpen; + GameSettings.SetCurrentConfig(config); GameSettings.SaveCurrentConfig(); GameMain.SoundManager.SetCategoryMuffle("default", false); GUI.ClearMessages(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs index e44c75dd2..8f843ba7c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs @@ -112,15 +112,17 @@ namespace Barotrauma.Steam var toUninstall = ContentPackageManager.WorkshopPackages.Where(p => p.SteamWorkshopId == workshopItem.Id) .ToHashSet(); + ContentPackageManager.EnabledPackages.DisableMods(toUninstall); toUninstall.Select(p => p.Dir).ForEach(d => Directory.Delete(d)); ContentPackageManager.WorkshopPackages.Refresh(); ContentPackageManager.EnabledPackages.DisableRemovedMods(); } - public static async Task ForceRedownload(Steamworks.Ugc.Item item) + public static async Task ForceRedownload(Steamworks.Ugc.Item item, CancellationTokenSource? cancellationTokenSrc = null) { NukeDownload(item); - await item.DownloadAsync(); + cancellationTokenSrc ??= new CancellationTokenSource(); + await item.DownloadAsync(ct: cancellationTokenSrc.Token); } /// diff --git a/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs index 30054648d..2b1722674 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Text/TextManager.cs @@ -57,6 +57,20 @@ namespace Barotrauma return isCJK.IsMatch(text); } + /// + /// Check if the currently selected language is available, and switch to English if not + /// + public static void VerifyLanguageAvailable() + { + if (!TextPacks.ContainsKey(GameSettings.CurrentConfig.Language)) + { + DebugConsole.ThrowError($"Could not find the language \"{GameSettings.CurrentConfig.Language}\". Trying to switch to English..."); + var config = GameSettings.CurrentConfig; + config.Language = "English".ToLanguageIdentifier();; + GameSettings.SetCurrentConfig(config); + } + } + public static bool ContainsTag(string tag) { return ContainsTag(tag.ToIdentifier()); diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index b47978352..e2b509a75 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,65 @@ +--------------------------------------------------------------------------------------------------------- +v0.17.6.0 +--------------------------------------------------------------------------------------------------------- + +Changes: +- Buffed ethanol's and tobacco's effects. +- Renamed "details" to "manage" and "permissions" to "rank" in the client management context menu to make them a little more clear. +- Added an indicator for when players are downloading files from the server to the player list in the lobby. +- Adjustments, tweaks, and polish for the new abyss monster, now called "Latcher". Updated texture. +- Adjusted the kill hammerhead missions. +- Changes to character aiming behavior. +- Giant Spineling doesn't flee anymore when being shot with coilgun, chaingun, or small arms. + +Changes (unstable only): +- Added separate icons for mods that you've published and mods that you've subscribed to. +- Added a button to the prompt asking you to download mods from the server that will subscribe to missing Workshop items. +- Added a context menu to the items in the Installed Mods tab. +- Added a button to update all mods that are out of date. +- Added a search box to the locked mods list. +- Added a search box to the required mods list in the submarine editor. +- Double-clicking now enables/disables items in the Installed Mods tab. + +Fixes: +- Fixed swimming characters sometimes being unable to stand up on stairs/platforms even if the water is shallow enough. +- Fixed guitar and harmonica being rendered on top of the water effect. +- Fixed guitar, harmonica, accordion and captains pipe having neutral buoyancy. +- Fixed mid-round joining clients not seeing subs purchased during that round. +- Fixed research station being repairable by clicking on it instead of pressing E. +- Fixed medical curtains disappearing before they're off-screen. +- Fixed karma preset being forced to default when starting a new server. +- Fixed calyxanide not damaging the "naturally spawning" husks. +- Fixed Herja's rear motion detector being connected to an incorrect display, and the bottom turret display having an incorrect text. +- Fixed crash caused by selection not being cleared when autocompleting or running a console command. +- Waypointfixes on abandoned outpost modules, some regular outpost modules, and Winterhalter. +- Fixed bots occasionally getting stuck while climbing ladders connecting outpost modules. +- Fixes to waypoint generator, mainly on stairs. +- Fixed a null reference exception when a bot is dismissed while being told to follow the player and still in the combat state. +- Fixed Giant Spineling targeting doors after being attacked, which it shouldn't do by design. Might affect other creatures too. + +Fixes (unstable only): +- Fixes and improvements to colony modules. +- Fixed items in vending machines not being displayed as "out of stock" client-side, fixed money getting deducted when trying to buy an item that's out of stock. +- Fixed crashing on startup if the selected language cannot be found (e.g. if you've previously used a modded language and no longer have that mod installed). +- Fixed chat messages about money transfer votes not showing up. +- Fixed voting not finishing until the timer has elapsed even if there's already enough yes/no votes. +- Fixed vitality multipliers not working (e.g. damage to the head not having a bigger effect than damage to the limbs). +- Fixed "create level object" crashing the level editor. +- Fixed crashing when trying to save a sub with whitespace at the end of the name. +- Fixed sub editor's tag picker not working. +- Fixed event sprites not appearing. +- Fixed submarine switching not working. +- Fixed crew list and chatbox refusing to stay closed. +- Fixed character names being in upper case when using the health scanner. +- Fixed explosive coilgun ammo not being sold by armory merchants. +- Fixed physicorium not being sold at research outposts. +- Fixed issues with store interface displaying incorrect information. +- Fixed issues with buying items in multiplayer campaign. +- Fixed issues with generating daily specials and requested goods for campaign stores. +- Fixed double title in mod download prompt. +- Fixed mod transfer skipping item assemblies. +- Waypoint fixes on new colony modules. + --------------------------------------------------------------------------------------------------------- v0.17.5.0 --------------------------------------------------------------------------------------------------------- diff --git a/Libraries/Facepunch.Steamworks/SteamUgc.cs b/Libraries/Facepunch.Steamworks/SteamUgc.cs index db06eeae8..691bf0688 100644 --- a/Libraries/Facepunch.Steamworks/SteamUgc.cs +++ b/Libraries/Facepunch.Steamworks/SteamUgc.cs @@ -99,11 +99,22 @@ namespace Steamworks onDownloadStarted = (r, id) => downloadStarted = true; OnDownloadItemResult += onDownloadStarted; + int iters = 0; while ( downloadStarted == false ) { - if ( ct.IsCancellationRequested ) - break; + ct.ThrowIfCancellationRequested(); + iters++; + if (iters >= 1000 / milisecondsUpdateDelay) + { + if (!item.IsDownloading && !item.IsInstalled) + { + //force download to start if it's not started + if ( Download( fileId, highPriority: true ) == false ) + return item.IsInstalled; + } + iters = 0; + } await Task.Delay( milisecondsUpdateDelay ); } } @@ -120,8 +131,7 @@ namespace Steamworks { while ( true ) { - if ( ct.IsCancellationRequested ) - break; + ct.ThrowIfCancellationRequested(); progress?.Invoke( 0.2f + item.DownloadAmount * 0.8f );