From 44ded0225a193452d9256a1b665ea0de5d3747d7 Mon Sep 17 00:00:00 2001
From: Markus Isberg <3e849f2e5c@pm.me>
Date: Wed, 30 Mar 2022 01:20:59 +0900
Subject: [PATCH] Unstable 0.17.5.0
---
.../ClientSource/Characters/Character.cs | 10 +-
.../Transition/UgcTransition.cs | 225 ++++++++++++
.../ClientSource/DebugConsole.cs | 52 ---
.../ClientSource/GUI/CrewManagement.cs | 6 +-
.../BarotraumaClient/ClientSource/GUI/GUI.cs | 26 +-
.../ClientSource/GUI/GUITextBox.cs | 5 +-
.../ClientSource/GUI/Store.cs | 88 ++---
.../ClientSource/GUI/TabMenu.cs | 347 +++++++++++-------
.../ClientSource/GUI/UpgradeStore.cs | 30 +-
.../ClientSource/GUI/VotingInterface.cs | 160 +++++---
.../BarotraumaClient/ClientSource/GameMain.cs | 8 +-
.../ClientSource/GameSession/CargoManager.cs | 2 +
.../GameSession/GameModes/CampaignMode.cs | 24 +-
.../GameModes/MultiPlayerCampaign.cs | 6 +-
.../GameModes/SinglePlayerCampaign.cs | 4 +-
.../ClientSource/GameSession/GameSession.cs | 1 +
.../ClientSource/GameSession/RoundSummary.cs | 2 +-
.../Items/Components/ItemComponent.cs | 14 +-
.../Items/Components/Machines/Fabricator.cs | 47 ++-
.../Items/Components/Machines/MiniMap.cs | 6 +-
.../ClientSource/Map/Map/Map.cs | 2 +-
.../ClientSource/Networking/GameClient.cs | 68 ++--
.../ClientSource/Networking/Voting.cs | 319 ++++++++--------
.../CampaignSetupUI/CampaignSetupUI.cs | 58 +++
.../MultiPlayerCampaignSetupUI.cs | 75 +---
.../SinglePlayerCampaignSetupUI.cs | 74 +---
.../ClientSource/Screens/CampaignUI.cs | 22 +-
.../Screens/CharacterEditor/Wizard.cs | 11 +-
.../ClientSource/Screens/EditorScreen.cs | 2 +
.../ClientSource/Screens/MainMenuScreen.cs | 9 +-
.../ClientSource/Screens/NetLobbyScreen.cs | 93 +----
.../ClientSource/Screens/ServerListScreen.cs | 30 +-
.../ClientSource/Screens/SubEditorScreen.cs | 30 +-
.../ClientSource/Settings/SettingsMenu.cs | 8 +-
.../Steam/{ => WorkshopMenu}/BBCode.cs | 8 +-
.../Immutable/ImmutableWorkshopMenu.cs | 41 +++
.../{ => WorkshopMenu/Mutable}/ItemList.cs | 2 +-
.../Mutable/MutableWorkshopMenu.cs} | 18 +-
.../{ => WorkshopMenu/Mutable}/PublishTab.cs | 2 +-
.../Steam/{ => WorkshopMenu}/UiUtil.cs | 24 +-
.../Steam/WorkshopMenu/WorkshopMenu.cs | 12 +
.../BarotraumaClient/LinuxClient.csproj | 2 +-
Barotrauma/BarotraumaClient/MacClient.csproj | 2 +-
.../BarotraumaClient/WindowsClient.csproj | 2 +-
.../BarotraumaServer/LinuxServer.csproj | 2 +-
Barotrauma/BarotraumaServer/MacServer.csproj | 2 +-
.../GameModes/CharacterCampaignData.cs | 2 +-
.../GameModes/MultiPlayerCampaign.cs | 248 ++++++-------
.../ServerSource/GameSession/MedicalClinic.cs | 2 +-
.../ServerSource/Networking/GameServer.cs | 160 ++++----
.../ServerSource/Networking/ServerSettings.cs | 4 +-
.../ServerSource/Networking/Voting.cs | 268 ++++++++++----
.../ServerSource/Screens/NetLobbyScreen.cs | 2 +-
.../BarotraumaServer/WindowsServer.csproj | 2 +-
.../Data/permissionpresets.xml | 2 +-
.../SharedSource/Characters/Character.cs | 9 +-
.../ContentPackageManager.cs | 20 +-
.../ContentManagement/ContentXElement.cs | 18 +
.../BarotraumaShared/SharedSource/Enums.cs | 11 +-
.../SharedSource/Events/Missions/Mission.cs | 16 +-
.../Events/Missions/MissionPrefab.cs | 3 +-
.../SharedSource/GameSession/CargoManager.cs | 1 +
.../GameSession/Data/Reputation.cs | 2 +-
.../SharedSource/GameSession/Data/Wallet.cs | 15 +-
.../GameSession/GameModes/CampaignMode.cs | 37 +-
.../GameModes/MultiPlayerCampaign.cs | 4 +-
.../SharedSource/GameSession/GameSession.cs | 62 +++-
.../Items/Components/Machines/Controller.cs | 93 +++--
.../Items/Components/Machines/Fabricator.cs | 30 +-
.../SharedSource/Items/Item.cs | 11 +-
.../SharedSource/Items/ItemPrefab.cs | 15 +-
.../SharedSource/Map/Levels/Level.cs | 8 +-
.../Map/Levels/LevelGenerationParams.cs | 4 +-
.../SharedSource/Map/Map/Location.cs | 12 +-
.../SharedSource/Map/Map/LocationType.cs | 37 +-
.../SharedSource/Map/Map/Map.cs | 41 ++-
.../Map/Outposts/OutpostGenerator.cs | 5 +
.../SharedSource/Map/SubmarineInfo.cs | 79 +---
.../Networking/ClientPermissions.cs | 7 +-
.../SharedSource/Networking/NetworkMember.cs | 7 +-
.../SharedSource/Networking/ServerSettings.cs | 85 +++--
.../SharedSource/Networking/Voting.cs | 26 +-
.../SharedSource/Steam/Workshop.cs | 10 +-
.../SharedSource/SteamAchievementManager.cs | 10 +-
.../SharedSource/{Map => Utils}/Md5Hash.cs | 0
.../SharedSource/Utils/NamedEvent.cs | 6 +
.../SharedSource/Utils/SaveUtil.cs | 58 ++-
Barotrauma/BarotraumaShared/changelog.txt | 50 +++
88 files changed, 2033 insertions(+), 1430 deletions(-)
create mode 100644 Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs
rename Barotrauma/BarotraumaClient/ClientSource/Steam/{ => WorkshopMenu}/BBCode.cs (96%)
create mode 100644 Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs
rename Barotrauma/BarotraumaClient/ClientSource/Steam/{ => WorkshopMenu/Mutable}/ItemList.cs (99%)
rename Barotrauma/BarotraumaClient/ClientSource/Steam/{WorkshopMenu.cs => WorkshopMenu/Mutable/MutableWorkshopMenu.cs} (97%)
rename Barotrauma/BarotraumaClient/ClientSource/Steam/{ => WorkshopMenu/Mutable}/PublishTab.cs (99%)
rename Barotrauma/BarotraumaClient/ClientSource/Steam/{ => WorkshopMenu}/UiUtil.cs (79%)
create mode 100644 Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs
rename Barotrauma/BarotraumaShared/SharedSource/{Map => Utils}/Md5Hash.cs (100%)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
index 4bd56bce4..0891e5008 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
@@ -1151,15 +1151,7 @@ namespace Barotrauma
}
}
- partial void OnMoneyChanged(int prevAmount, int newAmount)
- {
- if (newAmount > prevAmount)
- {
- int increase = newAmount - prevAmount;
- AddMessage("+" + TextManager.GetWithVariable("currencyformat", "[credits]", "[value]").Value,
- GUIStyle.Yellow, playSound: this == Controlled, "money".ToIdentifier(), increase);
- }
- }
+ partial void OnMoneyChanged(int prevAmount, int newAmount) { }
partial void OnTalentGiven(TalentPrefab talentPrefab)
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs b/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs
new file mode 100644
index 000000000..026b93043
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/ClientSource/ContentManagement/Transition/UgcTransition.cs
@@ -0,0 +1,225 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Barotrauma.Extensions;
+using Barotrauma.Steam;
+using Microsoft.Xna.Framework;
+using Directory = Barotrauma.IO.Directory;
+using File = Barotrauma.IO.File;
+using Path = Barotrauma.IO.Path;
+
+namespace Barotrauma.Transition
+{
+ ///
+ /// Class dedicated to transitioning away from the old, shitty
+ /// Mods + Submarines folders to the new LocalMods folder
+ ///
+ public static class UgcTransition
+ {
+ private const string readmeName = "LOCALMODS_README.txt";
+
+ public static void Prepare()
+ {
+ 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; }
+
+ var msgBox = new GUIMessageBox(TextManager.Get("Ugc.TransferTitle"), "", relativeSize: (0.5f, 0.8f),
+ buttons: new LocalizedString[] { TextManager.Get("Ugc.TransferButton") });
+
+ var desc = new GUITextBlock(new RectTransform((1.0f, 0.24f), msgBox.Content.RectTransform),
+ text: TextManager.Get("Ugc.TransferDesc"), wrap: true, textAlignment: Alignment.CenterLeft);
+
+ var modsList = new GUIListBox(new RectTransform((1.0f, 0.6f), msgBox.Content.RectTransform))
+ {
+ HoverCursor = CursorState.Default
+ };
+ Dictionary pathTickboxMap = new Dictionary();
+
+ void addHeader(LocalizedString str)
+ {
+ var itemFrame = new GUITextBlock(new RectTransform((1.0f, 0.08f), modsList.Content.RectTransform),
+ text: str, font: GUIStyle.SubHeadingFont)
+ {
+ CanBeFocused = false
+ };
+ }
+ void addTickbox(string dir, string name, bool ticked)
+ {
+ var itemFrame = new GUIFrame(new RectTransform((1.0f, 0.07f), modsList.Content.RectTransform),
+ style: null)
+ {
+ CanBeFocused = false
+ };
+ var tickbox = new GUITickBox(new RectTransform(Vector2.One, itemFrame.RectTransform), name)
+ {
+ Selected = ticked
+ };
+ pathTickboxMap.Add(dir, tickbox);
+ }
+
+ 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)));
+ }
+
+ addHeader("");
+ 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;
+
+ void createSubMsgBox(LocalizedString str, bool closable)
+ {
+ subMsgBox?.Close();
+ subMsgBox = new GUIMessageBox(headerText: "", text: str,
+ buttons: closable ? new[] { TextManager.Get("Close") } : Array.Empty());
+ if (closable)
+ {
+ subMsgBox.Buttons[0].OnClicked = subMsgBox.Close;
+ }
+ }
+
+ msgBox.Buttons[0].OnClicked = (b, o) =>
+ {
+ TaskPool.Add("TransferMods", TransferMods(pathTickboxMap), t2 =>
+ {
+ if (t2.Exception != null)
+ {
+ DebugConsole.ThrowError("There was an error transferring mods", t2.Exception.GetInnermost());
+ }
+ ContentPackageManager.LocalPackages.Refresh();
+ createSubMsgBox(TextManager.Get("Ugc.TransferComplete"), closable: true);
+ });
+ msgBox.Close();
+ createSubMsgBox(TextManager.Get("Ugc.Transferring"), closable: false);
+ return false;
+ };
+ });
+ }
+
+ private struct OldSubs
+ {
+ public readonly IReadOnlyList FilePaths;
+
+ public OldSubs(IReadOnlyList filePaths)
+ {
+ FilePaths = filePaths;
+ }
+ }
+
+ private struct OldMods
+ {
+ public readonly IReadOnlyList<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> Mods;
+
+ public OldMods(IReadOnlyList<(string Dir, string Name, Steamworks.Ugc.Item? Item, DateTime InstallTime)> mods)
+ {
+ Mods = mods;
+ }
+ }
+
+ private const string oldSubsPath = "Submarines";
+ private const string oldModsPath = "Mods";
+
+ private static async Task<(OldSubs Subs, OldMods Mods)> DetermineItemsToTransition()
+ {
+ string[] subs = 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[] allOldMods = Directory.GetDirectories(oldModsPath, "*", SearchOption.TopDirectoryOnly);
+
+ var publishedItems = await SteamManager.Workshop.GetPublishedItems();
+ foreach (var modDir in allOldMods)
+ {
+ var fileList = XMLExtensions.TryLoadXml(Path.Combine(modDir, ContentPackage.FileListFileName), out _);
+ if (fileList?.Root is null) { continue; }
+
+ var oldId = fileList.Root.GetAttributeUInt64("steamworkshopid", 0);
+ var updateTime = File.GetLastWriteTime(modDir).ToUniversalTime();
+ var oldName = fileList.Root.GetAttributeString("name", "");
+
+ var item = oldId != 0 ? publishedItems.FirstOrNull(it => it.Id == oldId) : null;
+ if (oldId == 0 || item.HasValue)
+ {
+ mods.Add((modDir, oldName, item, updateTime));
+ }
+ }
+ }
+
+ while (!(Screen.Selected is MainMenuScreen)) { await Task.Delay(500); }
+
+ return (new OldSubs(subs), new OldMods(mods));
+ }
+
+ private static bool FolderShouldBeTransitioned(string folderName)
+ {
+ return Directory.Exists(folderName)
+ && !File.Exists(Path.Combine(folderName, readmeName));
+ }
+
+ private static async Task TransferMods(Dictionary pathTickboxMap)
+ {
+ //WriteReadme(oldSubsPath); //can't do this because the 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")));
+
+ Directory.CreateDirectory(destPath);
+ File.Copy(path, Path.Combine(destPath, $"{dirName}.sub"));
+ 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; }
+ File.WriteAllText(path: Path.Combine(folderName, readmeName),
+ contents: "This folder is no longer used by Barotrauma;\n" +
+ "your mods and submarines should have been transferred\n" +
+ "to LocalMods. If they are not being found, delete this\n" +
+ "readme and relaunch the game.", encoding: Encoding.UTF8);
+ }
+ }
+}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
index f653a1a83..57d4bc38f 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
@@ -1101,58 +1101,6 @@ namespace Barotrauma
}
}, isCheat: true));
- commands.Add(new Command("save|savesub", "save [submarine name]: Save the currently loaded submarine using the specified name.", (string[] args) =>
- {
- if (args.Length < 1) { return; }
-
- GameMain.SubEditorScreen.SetMode(SubEditorScreen.Mode.Default);
-
- string fileName = string.Join(" ", args);
- if (fileName.Contains("../"))
- {
- ThrowError("Illegal symbols in filename (../)");
- return;
- }
-
- if (Submarine.MainSub.TrySaveAs(Barotrauma.IO.Path.Combine(SubmarineInfo.SavePath, fileName + ".sub")))
- {
- NewMessage("Sub saved", Color.Green);
- }
- }));
-
- commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) =>
- {
- if (args.Length == 0) { return; }
-
- if (GameMain.GameSession != null)
- {
- ThrowError("The loadsub command cannot be used when a round is running. You should probably be using spawnsub instead.");
- return;
- }
-
- string name = string.Join(" ", args);
- SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => name.Equals(s.Name, StringComparison.OrdinalIgnoreCase));
- if (subInfo == null)
- {
- string path = Path.Combine(SubmarineInfo.SavePath, name);
- if (!File.Exists(path))
- {
- ThrowError($"Could not find a submarine with the name \"{name}\" or in the path {path}.");
- return;
- }
- subInfo = new SubmarineInfo(path);
- }
-
- Submarine.Load(subInfo, true);
- },
- () =>
- {
- return new string[][]
- {
- SubmarineInfo.SavedSubmarines.Select(s => s.Name).ToArray()
- };
- }));
-
commands.Add(new Command("cleansub", "", (string[] args) =>
{
for (int i = MapEntity.mapEntityList.Count - 1; i >= 0; i--)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs
index 17f493e12..37935913a 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs
@@ -22,7 +22,7 @@ namespace Barotrauma
private GUIButton clearAllButton;
private List PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
- private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
+ private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign(ClientPermissions.ManageHires);
private Point resolutionWhenCreated;
@@ -433,7 +433,7 @@ namespace Barotrauma
else if (!btn.Enabled)
{
btn.ToolTip = string.Empty;
- btn.Enabled = true;
+ btn.Enabled = HasPermission;
}
};
@@ -632,7 +632,7 @@ namespace Barotrauma
totalBlock.Text = TextManager.FormatCurrency(total);
bool enoughMoney = campaign == null || campaign.Wallet.CanAfford(total);
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
- validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
+ validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
}
public bool ValidateHires(List hires, bool createNetworkEvent = false)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
index cba0182cf..88b835ede 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
@@ -316,26 +316,27 @@ namespace Barotrauma
if (GameMain.ShowPerf)
{
+ int x = 400;
int y = 10;
- DrawString(spriteBatch, new Vector2(300, y),
+ DrawString(spriteBatch, new Vector2(x, y),
"Draw - Avg: " + GameMain.PerformanceCounter.DrawTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.DrawTimeGraph.LargestValue().ToString("0.00") + " ms",
GUIStyle.Green, Color.Black * 0.8f, font: GUIStyle.SmallFont);
y += 15;
- GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: GUIStyle.Green);
+ GameMain.PerformanceCounter.DrawTimeGraph.Draw(spriteBatch, new Rectangle(x, y, 170, 50), color: GUIStyle.Green);
y += 50;
- DrawString(spriteBatch, new Vector2(300, y),
+ DrawString(spriteBatch, new Vector2(x, y),
"Update - Avg: " + GameMain.PerformanceCounter.UpdateTimeGraph.Average().ToString("0.00") + " ms" +
" Max: " + GameMain.PerformanceCounter.UpdateTimeGraph.LargestValue().ToString("0.00") + " ms",
Color.LightBlue, Color.Black * 0.8f, font: GUIStyle.SmallFont);
y += 15;
- GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(300, y, 170, 50), color: Color.LightBlue);
+ GameMain.PerformanceCounter.UpdateTimeGraph.Draw(spriteBatch, new Rectangle(x, y, 170, 50), color: Color.LightBlue);
y += 50;
foreach (string key in GameMain.PerformanceCounter.GetSavedIdentifiers)
{
float elapsedMillisecs = GameMain.PerformanceCounter.GetAverageElapsedMillisecs(key);
- DrawString(spriteBatch, new Vector2(300, y),
+ DrawString(spriteBatch, new Vector2(x, y),
key + ": " + elapsedMillisecs.ToString("0.00"),
Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15;
@@ -351,18 +352,19 @@ namespace Barotrauma
if (Powered.Grids != null)
{
- DrawString(spriteBatch, new Vector2(300, y), "Grids: " + Powered.Grids.Count, Color.LightGreen, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y), "Grids: " + Powered.Grids.Count, Color.LightGreen, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
y += 15;
}
if (Settings.EnableDiagnostics)
{
- DrawString(spriteBatch, new Vector2(320, y), "ContinuousPhysicsTime: " + GameMain.World.ContinuousPhysicsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContinuousPhysicsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
- DrawString(spriteBatch, new Vector2(320, y + 15), "ControllersUpdateTime: " + GameMain.World.ControllersUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ControllersUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
- DrawString(spriteBatch, new Vector2(320, y + 30), "AddRemoveTime: " + GameMain.World.AddRemoveTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.AddRemoveTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
- DrawString(spriteBatch, new Vector2(320, y + 45), "NewContactsTime: " + GameMain.World.NewContactsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.NewContactsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
- DrawString(spriteBatch, new Vector2(320, y + 60), "ContactsUpdateTime: " + GameMain.World.ContactsUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContactsUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
- DrawString(spriteBatch, new Vector2(320, y + 75), "SolveUpdateTime: " + GameMain.World.SolveUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.SolveUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ x += 20;
+ DrawString(spriteBatch, new Vector2(x, y), "ContinuousPhysicsTime: " + GameMain.World.ContinuousPhysicsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContinuousPhysicsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y + 15), "ControllersUpdateTime: " + GameMain.World.ControllersUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ControllersUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y + 30), "AddRemoveTime: " + GameMain.World.AddRemoveTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.AddRemoveTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y + 45), "NewContactsTime: " + GameMain.World.NewContactsTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.NewContactsTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y + 60), "ContactsUpdateTime: " + GameMain.World.ContactsUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.ContactsUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
+ DrawString(spriteBatch, new Vector2(x, y + 75), "SolveUpdateTime: " + GameMain.World.SolveUpdateTime.TotalMilliseconds, Color.Lerp(Color.LightGreen, GUIStyle.Red, (float)GameMain.World.SolveUpdateTime.TotalMilliseconds / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
}
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
index 7a096a1a6..4635c2150 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBox.cs
@@ -534,7 +534,10 @@ namespace Barotrauma
}
}
Vector2 finalBottomRight = characterPositions[endIndex];
- finalBottomRight += Font.MeasureChar(Text[endIndex]) * TextBlock.TextScale;
+ if (Text.Length > endIndex)
+ {
+ finalBottomRight += Font.MeasureChar(Text[endIndex]) * TextBlock.TextScale;
+ }
drawRect(topLeft, finalBottomRight);
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs
index 7d408efd1..88bf466e4 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs
@@ -113,71 +113,40 @@ namespace Barotrauma
#region Permissions
- private bool hadPermissions, hadBuyPermissions, hadSellInventoryPermissions, hadSellSubPermissions;
+ private bool hadBuyPermissions, hadSellInventoryPermissions, hadSellSubPermissions;
- private bool HasPermissions
- {
- get => GetPermissions();
- set => hadPermissions = value;
- }
private bool HasBuyPermissions
{
- get => HasPermissions || GetPermissions(StoreTab.Buy);
+ get => HasPermissionToUseTab(StoreTab.Buy);
set => hadBuyPermissions = value;
}
private bool HasSellInventoryPermissions
{
- get => HasPermissions || GetPermissions(StoreTab.Sell);
+ get => HasPermissionToUseTab(StoreTab.Sell);
set => hadSellInventoryPermissions = value;
}
private bool HasSellSubPermissions
{
- get => HasPermissions || GetPermissions(StoreTab.SellSub);
+ get => HasPermissionToUseTab(StoreTab.SellSub);
set => hadSellSubPermissions = value;
}
- private bool GetPermissions(StoreTab? tab = null)
+ private bool HasPermissionToUseTab(StoreTab tab)
{
- if (!tab.HasValue)
+ return tab switch
{
- return campaignUI.Campaign.AllowedToManageCampaign() || campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.CampaignStore);
- }
- else
- {
- return tab.Value switch
- {
- StoreTab.Buy => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.BuyItems),
- StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
- StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
- _ => false,
- };
- }
+ StoreTab.Buy => true,
+ StoreTab.Sell => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellInventoryItems),
+ StoreTab.SellSub => campaignUI.Campaign.AllowedToManageCampaign(Networking.ClientPermissions.SellSubItems),
+ _ => false,
+ };
}
- private void UpdatePermissions(StoreTab? tab = null)
+ private void UpdatePermissions()
{
- HasPermissions = GetPermissions();
- if (!tab.HasValue)
- {
- HasBuyPermissions = GetPermissions(StoreTab.Buy);
- HasSellInventoryPermissions = GetPermissions(StoreTab.Sell);
- HasSellSubPermissions = GetPermissions(StoreTab.SellSub);
- }
- else
- {
- switch (tab.Value)
- {
- case StoreTab.Buy:
- HasBuyPermissions = GetPermissions(tab.Value);
- break;
- case StoreTab.Sell:
- HasSellInventoryPermissions = GetPermissions(tab.Value);
- break;
- case StoreTab.SellSub:
- HasSellSubPermissions = GetPermissions(tab.Value);
- break;
- }
- }
+ HasBuyPermissions = HasPermissionToUseTab(StoreTab.Buy);
+ HasSellInventoryPermissions = HasPermissionToUseTab(StoreTab.Sell);
+ HasSellSubPermissions = HasPermissionToUseTab(StoreTab.SellSub);
}
private bool HasTabPermissions(StoreTab tab)
@@ -196,23 +165,16 @@ namespace Barotrauma
return HasTabPermissions(activeTab);
}
- private bool HavePermissionsChanged(StoreTab? tab = null)
+ private bool HavePermissionsChanged(StoreTab tab)
{
- if (!tab.HasValue)
+ bool hadTabPermissions = tab switch
{
- return hadPermissions != HasPermissions;
- }
- else
- {
- bool hadTabPermissions = tab.Value switch
- {
- StoreTab.Buy => hadBuyPermissions,
- StoreTab.Sell => hadSellInventoryPermissions,
- StoreTab.SellSub => hadSellSubPermissions,
- _ => false
- };
- return hadTabPermissions != HasTabPermissions(tab.Value);
- }
+ StoreTab.Buy => hadBuyPermissions,
+ StoreTab.Sell => hadSellInventoryPermissions,
+ StoreTab.SellSub => hadSellSubPermissions,
+ _ => false
+ };
+ return hadTabPermissions != HasTabPermissions(tab);
}
#endregion
@@ -2202,10 +2164,6 @@ namespace Barotrauma
{
RefreshItemsToSellFromSub();
}
- if (needsRefresh || HavePermissionsChanged())
- {
- Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f);
- }
if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy))
{
RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f);
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs
index abd866462..da6b8d6d1 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs
@@ -43,6 +43,7 @@ namespace Barotrauma
private GUIButton transferMenuButton;
private float transferMenuOpenState;
private bool transferMenuStateCompleted;
+ private readonly HashSet registeredEvents = new HashSet();
private class LinkedGUI
{
@@ -218,7 +219,7 @@ namespace Barotrauma
public void AddToGUIUpdateList()
{
- infoFrame?.AddToGUIUpdateList(order: 1);
+ infoFrame?.AddToGUIUpdateList();
NetLobbyScreen.JobInfoFrame?.AddToGUIUpdateList();
}
@@ -298,11 +299,13 @@ namespace Barotrauma
}
SetBalanceText(balanceText, campaignMode.Bank.Balance);
- campaignMode.OnMoneyChanged.RegisterOverwriteExisting(nameof(CreateInfoFrame).ToIdentifier(), e =>
+ Identifier eventIdentifier = nameof(CreateInfoFrame).ToIdentifier();
+ campaignMode.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
{
- if (e.Wallet != campaignMode.Bank) { return; }
+ if (!e.Owner.IsNone()) { return; }
SetBalanceText(balanceText, e.Wallet.Balance);
});
+ registeredEvents.Add(eventIdentifier);
static void SetBalanceText(GUITextBlock text, int balance)
{
@@ -371,15 +374,16 @@ namespace Barotrauma
}
}
- private const float jobColumnWidthPercentage = 0.138f;
- private const float characterColumnWidthPercentage = 0.656f;
- private const float pingColumnWidthPercentage = 0.206f;
+ private const float jobColumnWidthPercentage = 0.138f,
+ characterColumnWidthPercentage = 0.45f,
+ pingColumnWidthPercentage = 0.206f,
+ walletColumnWidthPercentage = 0.206f;
- private int jobColumnWidth, characterColumnWidth, pingColumnWidth;
+ private int jobColumnWidth, characterColumnWidth, pingColumnWidth, walletColumnWidth;
private void CreateCrewListFrame(GUIFrame crewFrame)
{
- crew = GameMain.GameSession?.CrewManager?.GetCharacters() ?? Array.Empty();
+ crew = GameMain.GameSession?.CrewManager?.GetCharacters() ?? new List() { TestScreen.dummyCharacter};
teamIDs = crew.Select(c => c.TeamID).Distinct().ToList();
// Show own team first when there's more than one team
@@ -553,20 +557,23 @@ namespace Barotrauma
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
GUIButton pingButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("serverlistping"), style: "GUIButtonSmallFreeScale");
+ GUIButton walletButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("crewwallet.wallet"), style: "GUIButtonSmallFreeScale");
sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f);
pingButton.RectTransform.RelativeSize = new Vector2(pingColumnWidthPercentage * sizeMultiplier, 1f);
+ walletButton.RectTransform.RelativeSize = new Vector2(walletColumnWidthPercentage * sizeMultiplier, 1f);
- jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = GUIStyle.HotkeyFont;
- jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = false;
- jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = ForceUpperCase.Yes;
+ jobButton.TextBlock.Font = characterButton.TextBlock.Font = pingButton.TextBlock.Font = walletButton.TextBlock.Font = GUIStyle.HotkeyFont;
+ jobButton.CanBeFocused = characterButton.CanBeFocused = pingButton.CanBeFocused = walletButton.CanBeFocused = false;
+ jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = pingButton.ForceUpperCase = walletButton.ForceUpperCase = ForceUpperCase.Yes;
jobColumnWidth = jobButton.Rect.Width;
characterColumnWidth = characterButton.Rect.Width;
pingColumnWidth = pingButton.Rect.Width;
+ walletColumnWidth = walletButton.Rect.Width;
}
private void CreateMultiPlayerList(bool refresh)
@@ -651,6 +658,8 @@ namespace Barotrauma
};
}
}
+
+ CreateWalletCrewFrame(character, paddedFrame);
}
private void CreateMultiPlayerClientElement(Client client)
@@ -678,6 +687,10 @@ namespace Barotrauma
};
CreateNameWithPermissionIcon(client, paddedFrame);
+ if (client.Character is { } character)
+ {
+ CreateWalletCrewFrame(character, paddedFrame);
+ }
linkedGUIList.Add(new LinkedGUI(client, frame, false, new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center)));
}
@@ -714,6 +727,47 @@ namespace Barotrauma
return 0;
}
+ private void CreateWalletCrewFrame(Character character, GUILayoutGroup paddedFrame)
+ {
+ GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(new Point(walletColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), childAnchor: Anchor.Center)
+ {
+ CanBeFocused = false
+ };
+
+ if (character.IsBot) { return; }
+
+ GUILayoutGroup paddedLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1f), walletLayout.RectTransform, Anchor.Center), isHorizontal: true)
+ {
+ Stretch = true
+ };
+
+ GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "StoreTradingIcon", scaleToFit: true);
+ GUITextBlock walletBlock = new GUITextBlock(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), string.Empty, textAlignment: Alignment.Right, font: GUIStyle.SubHeadingFont);
+ SetWalletText(walletBlock, character.Wallet);
+
+ if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign)
+ {
+ Identifier eventIdentifier = new Identifier($"{nameof(CreateWalletCrewFrame)}.{character.ID}");
+ campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
+ {
+ if (!(e.Owner is Some { Value: var owner }) || owner != character) { return; }
+ SetWalletText(walletBlock, e.Wallet);
+ });
+ registeredEvents.Add(eventIdentifier);
+ }
+
+ static void SetWalletText(GUITextBlock block, Wallet wallet)
+ {
+ block.Text = TextManager.FormatCurrency(wallet.Balance);
+ block.ToolTip = string.Empty;
+ if (block.TextSize.X + block.Padding.X + block.Padding.Z > block.Rect.Width)
+ {
+ block.ToolTip = block.Text;
+ block.Text = TextManager.Get("crewwallet.balance.toomuchtoshow");
+ }
+ }
+ }
+
private void CreateNameWithPermissionIcon(Client client, GUILayoutGroup paddedFrame)
{
GUITextBlock characterNameBlock;
@@ -795,7 +849,7 @@ namespace Barotrauma
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
if (existingPreview != null) { infoFrameHolder.RemoveChild(existingPreview); }
- GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.145f, 0) })
+ GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.69f), infoFrameHolder.RectTransform, Anchor.TopRight, Pivot.TopLeft) { RelativeOffset = new Vector2(-0.061f, 0) })
{
UserData = "SelectedCharacter"
};
@@ -810,28 +864,29 @@ namespace Barotrauma
{
GUIComponent preview = character.Info.CreateInfoFrame(background, false, GetPermissionIcon(GameMain.Client.ConnectedClients.Find(c => c.Character == character)));
GameMain.Client.SelectCrewCharacter(character, preview);
- CreateWalletFrame(background, character);
+ if (!character.IsBot && GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign) { CreateWalletFrame(background, character, mpCampaign); }
}
}
else if (client != null)
{
GUIComponent preview = CreateClientInfoFrame(background, client, GetPermissionIcon(client));
GameMain.Client?.SelectCrewClient(client, preview);
- if (client.Character != null)
+ if (client.Character != null && GameMain.GameSession?.Campaign is MultiPlayerCampaign mpCampaign)
{
- CreateWalletFrame(background, client.Character);
+ CreateWalletFrame(background, client.Character, mpCampaign);
}
}
return true;
}
- private void CreateWalletFrame(GUIComponent parent, Character character)
+ private void CreateWalletFrame(GUIComponent parent, Character character, MultiPlayerCampaign campaign)
{
+ if (campaign is null) { throw new ArgumentNullException(nameof(campaign), "Tried to create a wallet frame when campaign was null"); }
if (character is null) { throw new ArgumentNullException(nameof(character), "Tried to create a wallet frame for a null character");}
isTransferMenuOpen = false;
transferMenuOpenState = 1f;
- ImmutableArray salaryCrew = Mission.GetSalaryEligibleCrew().Where(c => c != character).ToImmutableArray();
+ ImmutableHashSet salaryCrew = GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != character).ToImmutableHashSet();
Wallet targetWallet = character.Wallet;
@@ -905,8 +960,8 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center);
GUILayoutGroup centerButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 1f), buttonLayout.RectTransform), isHorizontal: true);
- GUIButton confirmButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), centerButtonLayout.RectTransform), TextManager.Get("confirm"), style: "GUIButtonFreeScale") { Enabled = false };
GUIButton resetButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), centerButtonLayout.RectTransform), TextManager.Get("reset"), style: "GUIButtonFreeScale") { Enabled = false };
+ GUIButton confirmButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), centerButtonLayout.RectTransform), TextManager.Get("confirm"), style: "GUIButtonFreeScale") { Enabled = false };
// @formatter:on
ImmutableArray layoutGroups = ImmutableArray.Create(transferMenuLayout, paddedTransferMenuLayout, mainLayout, leftLayout, rightLayout);
MedicalClinicUI.EnsureTextDoesntOverflow(character.Name, leftName, leftLayout.Rect, layoutGroups);
@@ -927,137 +982,146 @@ namespace Barotrauma
ToggleTransferMenuIcon(transferMenuButton, open: isTransferMenuOpen);
ToggleCenterButton(centerButton, isSending);
- if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign)
+
+ if (!(Character.Controlled is { } myCharacter))
{
- if (!(Character.Controlled is { } myCharacter))
- {
- salarySlider.Enabled = false;
- transferAmountInput.Enabled = false;
- centerButton.Enabled = false;
- confirmButton.Enabled = false;
- return;
- }
+ salarySlider.Enabled = false;
+ transferAmountInput.Enabled = false;
+ centerButton.Enabled = false;
+ confirmButton.Enabled = false;
+ return;
+ }
- bool hasPermissions = campaign.AllowedToManageCampaign();
- salarySlider.Enabled = hasPermissions;
- Wallet otherWallet;
+ bool hasMoneyPermissions = campaign.AllowedToManageCampaign(ClientPermissions.ManageMoney);
+ salarySlider.Enabled = hasMoneyPermissions;
+ Wallet otherWallet;
- switch (hasPermissions)
- {
- case true:
- rightName.Text = TextManager.Get("crewwallet.bank");
- otherWallet = campaign.Bank;
- break;
- case false when character == myCharacter:
- rightName.Text = TextManager.Get("crewwallet.bank");
- otherWallet = campaign.Bank;
- isSending = true;
- ToggleCenterButton(centerButton, isSending);
- break;
- default:
- rightName.Text = myCharacter.Name;
- otherWallet = campaign.PersonalWallet;
- break;
- }
+ switch (hasMoneyPermissions)
+ {
+ case true:
+ rightName.Text = TextManager.Get("crewwallet.bank");
+ otherWallet = campaign.Bank;
+ break;
+ case false when character == myCharacter:
+ rightName.Text = TextManager.Get("crewwallet.bank");
+ otherWallet = campaign.Bank;
+ isSending = true;
+ ToggleCenterButton(centerButton, isSending);
+ break;
+ default:
+ rightName.Text = myCharacter.Name;
+ otherWallet = campaign.PersonalWallet;
+ break;
+ }
- MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
-
- if (!hasPermissions)
+ MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
+ updateButtonText();
+ if (!hasMoneyPermissions)
+ {
+ if (character != Character.Controlled)
{
centerButton.Enabled = centerButton.CanBeFocused = false;
- salarySlider.Enabled = salarySlider.CanBeFocused = false;
}
+ salarySlider.Enabled = salarySlider.CanBeFocused = false;
+ }
- leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
+ leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
+ UpdateAllInputs();
+
+ centerButton.OnClicked = (btn, o) =>
+ {
+ isSending = !isSending;
+ updateButtonText();
+ ToggleCenterButton(btn, isSending);
UpdateAllInputs();
+ return true;
+ };
- centerButton.OnClicked = (btn, o) =>
+ void updateButtonText()
+ {
+ confirmButton.Text = TextManager.Get(hasMoneyPermissions || isSending ? "confirm" : "crewwallet.requestmoney");
+ }
+
+ transferAmountInput.OnValueChanged = input =>
+ {
+ UpdateInputs();
+ };
+
+ transferAmountInput.OnValueEntered = input =>
+ {
+ UpdateAllInputs();
+ };
+
+ Identifier eventIdentifier = nameof(CreateWalletFrame).ToIdentifier();
+ campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
+ {
+ if (e.Wallet == targetWallet)
{
- isSending = !isSending;
- ToggleCenterButton(btn, isSending);
- UpdateAllInputs();
- return true;
- };
-
- transferAmountInput.OnValueChanged = input =>
- {
- UpdateInputs();
- };
-
- transferAmountInput.OnValueEntered = input =>
- {
- UpdateAllInputs();
- };
-
- campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(CreateWalletFrame).ToIdentifier(), e =>
- {
- if (e.Wallet == targetWallet)
- {
- moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
- salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
- }
- UpdateAllInputs();
- });
-
- resetButton.OnClicked = (button, o) =>
- {
- transferAmountInput.IntValue = 0;
- UpdateAllInputs();
- return true;
- };
-
- confirmButton.OnClicked = (button, o) =>
- {
- int amount = transferAmountInput.IntValue;
- if (amount == 0) { return false; }
-
- Option target1 = Option.Some(character),
- target2 = otherWallet == campaign.Bank ? Option.None() : Option.Some(myCharacter);
- if (isSending) { (target1, target2) = (target2, target1); }
-
- SendTransaction(target1, target2, amount);
- isTransferMenuOpen = false;
- ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
- return true;
- };
-
- void UpdateAllInputs()
- {
- UpdateInputs();
- UpdateMaxInput();
+ moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
+ salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
}
+ UpdateAllInputs();
+ });
+ registeredEvents.Add(eventIdentifier);
- void UpdateInputs()
- {
- confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
- if (transferAmountInput.IntValue == 0)
- {
- rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
- rightBalance.TextColor = GUIStyle.TextColorNormal;
- leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
- leftBalance.TextColor = GUIStyle.TextColorNormal;
- }
- else if (isSending)
- {
- rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
- rightBalance.TextColor = GUIStyle.Blue;
- leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
- leftBalance.TextColor = GUIStyle.Red;
- }
- else
- {
- rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
- rightBalance.TextColor = GUIStyle.Red;
- leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
- leftBalance.TextColor = GUIStyle.Blue;
- }
- }
+ resetButton.OnClicked = (button, o) =>
+ {
+ transferAmountInput.IntValue = 0;
+ UpdateAllInputs();
+ return true;
+ };
- void UpdateMaxInput()
+ confirmButton.OnClicked = (button, o) =>
+ {
+ int amount = transferAmountInput.IntValue;
+ if (amount == 0) { return false; }
+
+ Option target1 = Option.Some(character),
+ target2 = otherWallet == campaign.Bank ? Option.None() : Option.Some(myCharacter);
+ if (isSending) { (target1, target2) = (target2, target1); }
+
+ SendTransaction(target1, target2, amount);
+ isTransferMenuOpen = false;
+ ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
+ return true;
+ };
+
+ void UpdateAllInputs()
+ {
+ UpdateInputs();
+ UpdateMaxInput();
+ }
+
+ void UpdateInputs()
+ {
+ confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
+ if (transferAmountInput.IntValue == 0)
{
- transferAmountInput.MaxValueInt = isSending ? targetWallet.Balance : otherWallet.Balance;
+ rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
+ rightBalance.TextColor = GUIStyle.TextColorNormal;
+ leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
+ leftBalance.TextColor = GUIStyle.TextColorNormal;
}
+ else if (isSending)
+ {
+ rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
+ rightBalance.TextColor = GUIStyle.Blue;
+ leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
+ leftBalance.TextColor = GUIStyle.Red;
+ }
+ else
+ {
+ rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
+ rightBalance.TextColor = GUIStyle.Red;
+ leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
+ leftBalance.TextColor = GUIStyle.Blue;
+ }
+ }
+
+ void UpdateMaxInput()
+ {
+ transferAmountInput.MaxValueInt = isSending ? targetWallet.Balance : otherWallet.Balance;
}
static void ToggleTransferMenuIcon(GUIButton btn, bool open)
@@ -1173,7 +1237,7 @@ namespace Barotrauma
private void CreateMultiPlayerLogContent(GUIFrame crewFrame)
{
- var logContainer = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), crewFrame.RectTransform, Anchor.TopRight, Pivot.TopLeft) { RelativeOffset = new Vector2(-0.061f, 0) });
+ var logContainer = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.145f, 0) });
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.900f, 0.900f), logContainer.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0f, 0.0475f) }, style: null);
var content = new GUILayoutGroup(new RectTransform(Vector2.One, innerFrame.RectTransform))
{
@@ -1188,22 +1252,22 @@ namespace Barotrauma
Spacing = (int)(5 * GUI.Scale)
};
- foreach (Pair pair in storedMessages)
+ foreach ((string message, PlayerConnectionChangeType type) in storedMessages)
{
- AddLineToLog(pair.First, pair.Second);
+ AddLineToLog(message, type);
}
logList.BarScroll = 1f;
}
- private static readonly List> storedMessages = new List>();
+ private static readonly List<(string message, PlayerConnectionChangeType type)> storedMessages = new List<(string message, PlayerConnectionChangeType type)>();
public static void StorePlayerConnectionChangeMessage(ChatMessage message)
{
if (!GameMain.GameSession?.IsRunning ?? true) { return; }
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
- storedMessages.Add(new Pair(msg, message.ChangeType));
+ storedMessages.Add((msg, message.ChangeType));
if (GameSession.IsTabMenuOpen && SelectedTab == InfoFrameTab.Crew)
{
@@ -2026,5 +2090,14 @@ namespace Barotrauma
if (character != Character.Controlled) { return; }
UpdateTalentInfo();
}
+
+ public void OnClose()
+ {
+ if (!(GameMain.GameSession?.Campaign is { } campaign)) { return; }
+ foreach (Identifier identifier in registeredEvents)
+ {
+ campaign.OnMoneyChanged.TryDeregister(identifier);
+ }
+ }
}
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs
index a6a0f3a54..cbdf71c9f 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/UpgradeStore.cs
@@ -73,6 +73,8 @@ namespace Barotrauma
private Point screenResolution;
+ private bool needsRefresh = true;
+
///
/// While set to true any call to will cause the buy button to be disabled and to not update the prices.
/// This is to prevent us from buying another upgrade before the server has given us the new prices and causing potential syncing issues.
@@ -102,13 +104,18 @@ namespace Barotrauma
CreateUI(upgradeFrame);
if (Campaign == null) { return; }
- Campaign.UpgradeManager.OnUpgradesChanged += RefreshAll;
- Campaign.CargoManager.OnPurchasedItemsChanged += RefreshAll;
- Campaign.CargoManager.OnSoldItemsChanged += RefreshAll;
- Campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(UpgradeStore).ToIdentifier(), e => { RefreshAll(); } );
+ Campaign.UpgradeManager.OnUpgradesChanged += RequestRefresh;
+ Campaign.CargoManager.OnPurchasedItemsChanged += RequestRefresh;
+ Campaign.CargoManager.OnSoldItemsChanged += RequestRefresh;
+ Campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(UpgradeStore).ToIdentifier(), e => { RequestRefresh(); } );
}
- public void RefreshAll()
+ public void RequestRefresh()
+ {
+ needsRefresh = true;
+ }
+
+ private void RefreshAll()
{
switch (selectedUpgradeTab)
{
@@ -131,6 +138,7 @@ namespace Barotrauma
}
break;
}
+ needsRefresh = false;
}
private void RefreshUpgradeList()
@@ -1295,7 +1303,9 @@ namespace Barotrauma
{
if (Campaign == null) { return; }
- if (!parent.Children.Any() || Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine || GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
+ if (!parent.Children.Any() ||
+ Submarine.MainSub != null && Submarine.MainSub != drawnSubmarine ||
+ GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
{
GameMain.GameSession?.SubmarineInfo?.CheckSubsLeftBehind();
drawnSubmarine = Submarine.MainSub;
@@ -1313,6 +1323,10 @@ namespace Barotrauma
// we also need this when we first load in so we know which category entries to disable since the CampaignUI is created before the submarine is loaded in.
RefreshAll();
}
+ if (needsRefresh)
+ {
+ RefreshAll();
+ }
// accept an active confirmation popup if any
if (PlayerInput.KeyHit(Keys.Enter) && GUIMessageBox.MessageBoxes.Any())
@@ -1588,7 +1602,7 @@ namespace Barotrauma
if (button != null)
{
button.Enabled = currentLevel < prefab.MaxLevel;
- if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || !campaign.Wallet.CanAfford(price))
+ if (WaitForServerUpdate || !campaign.Wallet.CanAfford(price))
{
button.Enabled = false;
}
@@ -1693,7 +1707,7 @@ namespace Barotrauma
return frames.ToArray();
}
- private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
+ private bool HasPermission => true;
// just a shortcut to create new RectTransforms since all the new RectTransform and new Vector2 confuses my IDE (and me)
private static RectTransform rectT(float x, float y, GUIComponent parentComponent, Anchor anchor = Anchor.TopLeft, ScaleBasis scaleBasis = ScaleBasis.Normal)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs
index 4f5541602..a927e30a4 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/VotingInterface.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
@@ -22,27 +23,50 @@ namespace Barotrauma
private float votingTime = 100f;
private float timer;
private VoteType currentVoteType;
- private Color submarineColor => GUIStyle.Orange;
+ private Color SubmarineColor => GUIStyle.Orange;
private Point createdForResolution;
- public VotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
+ public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
{
- if (starter == null || info == null) return;
- SetSubmarineVotingText(starter, info, type);
- this.votingTime = votingTime;
- getYesVotes = SubmarineYesVotes;
- getNoVotes = SubmarineNoVotes;
- getMaxVotes = SubmarineMaxVotes;
- onVoteEnd = () => SendSubmarineVoteEndMessage(info, type);
+ if (starter == null || info == null) { return null; }
- Initialize(starter, type);
+ var subVoting = new VotingInterface()
+ {
+ votingTime = votingTime,
+ getYesVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountYes(type) ?? 0,
+ getNoVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountNo(type) ?? 0,
+ getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(type) ?? 0,
+ };
+ subVoting.onVoteEnd = () => subVoting.SendSubmarineVoteEndMessage(info, type);
+ subVoting.SetSubmarineVotingText(starter, info, type);
+ subVoting.Initialize(starter, type);
+ return subVoting;
}
+ public static VotingInterface CreateMoneyTransferVotingInterface(Client starter, Client from, Client to, int amount, float votingTime)
+ {
+ if (starter == null) { return null; }
+ if (from == null && to == null) { return null; }
+
+ var transferVoting = new VotingInterface()
+ {
+ votingTime = votingTime,
+ getYesVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountYes(VoteType.TransferMoney) ?? 0,
+ getNoVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountNo(VoteType.TransferMoney) ?? 0,
+ getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(VoteType.TransferMoney) ?? 0,
+ };
+ transferVoting.onVoteEnd = () => transferVoting.SendMoneyTransferVoteEndMessage(from, to, amount);
+ transferVoting.SetMoneyTransferVotingText(starter, from, to, amount);
+ transferVoting.Initialize(starter, VoteType.TransferMoney);
+ return transferVoting;
+ }
+
+
private void Initialize(Client starter, VoteType type)
{
currentVoteType = type;
CreateVotingGUI();
- if (starter.ID == GameMain.Client.ID) SetGUIToVotedState(2);
+ if (starter.ID == GameMain.Client.ID) { SetGUIToVotedState(2); }
VoteRunning = true;
}
@@ -50,7 +74,7 @@ namespace Barotrauma
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
- if (frame != null) frame.Parent.RemoveChild(frame);
+ frame?.Parent.RemoveChild(frame);
frame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.VotingArea, GameMain.Client.InGameHUD.RectTransform), style: "");
int padding = HUDLayoutSettings.Padding * 2;
@@ -116,8 +140,8 @@ namespace Barotrauma
public void Update(float deltaTime)
{
- if (!VoteRunning) return;
- if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateVotingGUI();
+ if (!VoteRunning) { return; }
+ if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) { CreateVotingGUI(); }
yesVotes = getYesVotes();
noVotes = getNoVotes();
maxVotes = getMaxVotes();
@@ -126,7 +150,6 @@ namespace Barotrauma
votingTimer.BarSize = timer / votingTime;
}
-
public void EndVote(bool passed, int yesVoteFinal, int noVoteFinal)
{
VoteRunning = false;
@@ -143,19 +166,20 @@ namespace Barotrauma
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
- string submarineRichString = $"‖color:{submarineColor.R},{submarineColor.G},{submarineColor.B}‖{info.DisplayName}‖color:end‖";
+ string submarineRichString = $"‖color:{SubmarineColor.R},{SubmarineColor.G},{SubmarineColor.B}‖{info.DisplayName}‖color:end‖";
+ LocalizedString text = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
- votingOnText = TextManager.GetWithVariables("submarinepurchaseandswitchvote",
+ text = TextManager.GetWithVariables("submarinepurchaseandswitchvote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
("[currencyname]", TextManager.Get("credit").ToLower()));
break;
case VoteType.PurchaseSub:
- votingOnText = TextManager.GetWithVariables("submarinepurchasevote",
+ text = TextManager.GetWithVariables("submarinepurchasevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
@@ -163,10 +187,9 @@ namespace Barotrauma
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
-
if (deliveryFee > 0)
{
- votingOnText = TextManager.GetWithVariables("submarineswitchfeevote",
+ text = TextManager.GetWithVariables("submarineswitchfeevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[locationname]", endLocation.Name),
@@ -175,37 +198,22 @@ namespace Barotrauma
}
else
{
- votingOnText = TextManager.GetWithVariables("submarineswitchnofeevote",
+ text = TextManager.GetWithVariables("submarineswitchnofeevote",
("[playername]", characterRichString),
("[submarinename]", submarineRichString));
}
break;
}
- votingOnText = RichString.Rich(votingOnText);
- }
-
- private int SubmarineYesVotes()
- {
- return GameMain.NetworkMember.SubmarineVoteYesCount;
- }
-
- private int SubmarineNoVotes()
- {
- return GameMain.NetworkMember.SubmarineVoteNoCount;
- }
-
- private int SubmarineMaxVotes()
- {
- return GameMain.NetworkMember.SubmarineVoteMax;
+ votingOnText = RichString.Rich(text);
}
private void SendSubmarineVoteEndMessage(SubmarineInfo info, VoteType type)
{
- GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes.ToString(), noVotes.ToString(), votePassed).Value, ChatMessageType.Server);
+ GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes, noVotes, votePassed).Value, ChatMessageType.Server);
}
- public static LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, string yesVoteString, string noVoteString, bool votePassed)
+ private LocalizedString GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, int yesVoteCount, int noVoteCount, bool votePassed)
{
LocalizedString result = string.Empty;
@@ -214,18 +222,18 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed",
("[submarinename]", info.DisplayName),
- ("[amount]", info.Price.ToString()),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
("[currencyname]", TextManager.Get("credit").ToLower()),
- ("[yesvotecount]", yesVoteString),
- ("[novotecount]" , noVoteString));
+ ("[yesvotecount]", yesVoteCount.ToString()),
+ ("[novotecount]" , noVoteCount.ToString()));
break;
case VoteType.PurchaseSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed",
("[submarinename]", info.DisplayName),
- ("[amount]", info.Price.ToString()),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", info.Price)),
("[currencyname]", TextManager.Get("credit").ToLower()),
- ("[yesvotecount]", yesVoteString),
- ("[novotecount]", noVoteString));
+ ("[yesvotecount]", yesVoteCount.ToString()),
+ ("[novotecount]", noVoteCount.ToString()));
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
@@ -235,17 +243,17 @@ namespace Barotrauma
result = TextManager.GetWithVariables(votePassed ? "submarineswitchfeevotepassed" : "submarineswitchfeevotefailed",
("[submarinename]", info.DisplayName),
("[locationname]", endLocation.Name),
- ("[amount]", deliveryFee.ToString()),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", deliveryFee)),
("[currencyname]", TextManager.Get("credit").ToLower()),
- ("[yesvotecount]", yesVoteString),
- ("[novotecount]", noVoteString));
+ ("[yesvotecount]", yesVoteCount.ToString()),
+ ("[novotecount]", noVoteCount.ToString()));
}
else
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed",
("[submarinename]", info.DisplayName),
- ("[yesvotecount]", yesVoteString),
- ("[novotecount]", noVoteString));
+ ("[yesvotecount]", yesVoteCount.ToString()),
+ ("[novotecount]", noVoteCount.ToString()));
}
break;
default:
@@ -255,6 +263,58 @@ namespace Barotrauma
}
#endregion
+
+ private void SetMoneyTransferVotingText(Client starter, Client from, Client to, int amount)
+ {
+ string name = starter.Name;
+ JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
+ Color nameColor = prefab != null ? prefab.UIColor : Color.White;
+ string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
+
+ LocalizedString text = string.Empty;
+ if (from == null && to != null)
+ {
+ text = TextManager.GetWithVariables("crewwallet.requestbanktoselfvote",
+ ("[requester]", characterRichString),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
+ }
+ else if (from != null && to == null)
+ {
+ text = TextManager.GetWithVariables("crewwallet.requestselftobankvote",
+ ("[requester]", characterRichString),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
+ }
+ else
+ {
+ //not supported atm: clients can only requests transfers between their own wallet and the bank
+ LocalizedString bankName = TextManager.Get("crewwallet.bank");
+ text = TextManager.GetWithVariables("crewwallet.requesttransfervote",
+ ("[requester]", characterRichString),
+ ("[player1]", from?.Character == null ? bankName : from.Character.Name),
+ ("[player2]", to?.Character == null ? bankName : to.Character.Name),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", amount)));
+ }
+
+ votingOnText = RichString.Rich(text);
+ }
+ private void SendMoneyTransferVoteEndMessage(Client from, Client to, int amount)
+ {
+ GameMain.NetworkMember.AddChatMessage(GetMoneyTransferVoteResultMessage(from, to, amount, yesVotes, noVotes, votePassed).Value, ChatMessageType.Server);
+ }
+
+ public static LocalizedString GetMoneyTransferVoteResultMessage(Client from, Client to, int transferAmount, int yesVoteCount, int noVoteCount, bool votePassed)
+ {
+ LocalizedString result = string.Empty;
+ if (from != null)
+ {
+ result = TextManager.GetWithVariables(votePassed ? "crewwallet.banktoplayer.votepassed" : "crewwallet.banktoplayer.votefailed",
+ ("[playername]", from.Name),
+ ("[amount]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", transferAmount)),
+ ("[yesvotecount]", yesVoteCount.ToString()),
+ ("[novotecount]", noVoteCount.ToString()));
+ }
+ return result;
+ }
public void Remove()
{
if (frame != null)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
index e4c8ab417..a046c051f 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
@@ -17,6 +17,7 @@ using Barotrauma.Tutorials;
using Barotrauma.Media;
using Barotrauma.Extensions;
using System.Threading.Tasks;
+using Barotrauma.Transition;
namespace Barotrauma
{
@@ -463,6 +464,7 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
+ UgcTransition.Prepare();
var contentPackageLoadRoutine = ContentPackageManager.Init();
foreach (var progress in contentPackageLoadRoutine)
{
@@ -691,14 +693,12 @@ namespace Barotrauma
}
else if (GameSettings.CurrentConfig.AutomaticCampaignLoadEnabled)
{
- IEnumerable saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
-
+ var saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
if (saveFiles.Count() > 0)
{
- saveFiles = saveFiles.OrderBy(file => File.GetLastWriteTime(file));
try
{
- SaveUtil.LoadGame(saveFiles.Last());
+ SaveUtil.LoadGame(saveFiles.OrderBy(file => file.SaveTime).Last().FilePath);
}
catch (Exception e)
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs
index 29cea3e9b..aa1b4528a 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CargoManager.cs
@@ -67,6 +67,8 @@ namespace Barotrauma
public void SetSoldItems(Dictionary> items)
{
+ if (SoldItems.Count == 0 && items.Count == 0) { return; }
+
SoldItems.Clear();
foreach (var entry in items)
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs
index 6d334c7a2..6b0927e40 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs
@@ -86,30 +86,14 @@ namespace Barotrauma
///
/// There is a server-side implementation of the method in
///
- public bool AllowedToEndRound()
- {
- //allow ending the round if the client has permissions, is the owner, the only client in the server
- //or if no-one has management permissions
- if (GameMain.Client == null) { return true; }
- return
- GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
- GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
- GameMain.Client.ConnectedClients.Count == 1 ||
- GameMain.Client.IsServerOwner ||
- GameMain.Client.ConnectedClients.None(c =>
- c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
- }
-
- ///
- /// There is a server-side implementation of the method in
- ///
- public bool AllowedToManageCampaign(ClientPermissions permissions = ClientPermissions.ManageCampaign)
+ public bool AllowedToManageCampaign(ClientPermissions permissions)
{
//allow managing the round if the client has permissions, is the owner, the only client in the server,
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(permissions) ||
+ GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
@@ -210,7 +194,7 @@ namespace Barotrauma
if (endRoundButton.Visible)
{
- if (!AllowedToEndRound())
+ if (!AllowedToManageCampaign(ClientPermissions.ManageMap))
{
buttonText = TextManager.Get("map");
}
@@ -306,7 +290,7 @@ namespace Barotrauma
default:
ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType, storeIdentifier: npc.MerchantIdentifier);
- CampaignUI.UpgradeStore?.RefreshAll();
+ CampaignUI.UpgradeStore?.RequestRefresh();
break;
}
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs
index 2847da846..3c2dd322e 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs
@@ -42,7 +42,7 @@ namespace Barotrauma
return PersonalWallet;
}
- public static void StartCampaignSetup(IEnumerable saveFiles)
+ public static void StartCampaignSetup(List saveFiles)
{
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
parent.ClearChildren();
@@ -746,7 +746,7 @@ namespace Barotrauma
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
- campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
+ campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var availableMission in availableMissions)
@@ -786,7 +786,7 @@ namespace Barotrauma
if (shouldRefresh)
{
- campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
+ campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
if (myCharacterInfo != null)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs
index ffa578204..53ba428fb 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs
@@ -110,7 +110,7 @@ namespace Barotrauma
petsElement = subElement;
break;
case Wallet.LowerCaseSaveElementName:
- Bank = new Wallet(subElement);
+ Bank = new Wallet(Option.None(), subElement);
break;
case "stats":
LoadStats(subElement);
@@ -129,7 +129,7 @@ namespace Barotrauma
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
- Bank = new Wallet
+ Bank = new Wallet(Option.None())
{
Balance = oldMoney
};
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs
index 0f794fa40..627f79236 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs
@@ -32,6 +32,7 @@ namespace Barotrauma
}
else
{
+ tabMenu?.OnClose();
tabMenu = null;
NetLobbyScreen.JobInfoFrame = null;
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs
index abe401df3..06e09c661 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs
@@ -318,7 +318,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
{
- var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, Mission.GetSalaryEligibleCrew().Where(c => c != controlled), Option.Some(reward));
+ var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option.Some(reward));
if (share > 0)
{
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs
index 02ad190c4..a189f5470 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/ItemComponent.cs
@@ -38,11 +38,14 @@ namespace Barotrauma.Items.Components
public readonly bool Loop;
- public ItemSound(RoundSound sound, ActionType type, bool loop = false)
+ public readonly bool OnlyPlayInSameSub;
+
+ public ItemSound(RoundSound sound, ActionType type, bool loop = false, bool onlyPlayInSameSub = false)
{
this.RoundSound = sound;
this.Type = type;
this.Loop = loop;
+ this.OnlyPlayInSameSub = onlyPlayInSameSub;
}
}
@@ -339,6 +342,11 @@ namespace Barotrauma.Items.Components
return;
}
+ if (itemSound.OnlyPlayInSameSub && item.Submarine != null && Character.Controlled != null)
+ {
+ if (Character.Controlled.Submarine == null || !Character.Controlled.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return; }
+ }
+
if (itemSound.Loop)
{
if (loopingSoundChannel != null && loopingSoundChannel.Sound != itemSound.RoundSound.Sound)
@@ -500,7 +508,9 @@ namespace Barotrauma.Items.Components
RoundSound sound = RoundSound.Load(subElement);
if (sound == null) { break; }
- ItemSound itemSound = new ItemSound(sound, type, subElement.GetAttributeBool("loop", false))
+ ItemSound itemSound = new ItemSound(sound, type,
+ subElement.GetAttributeBool("loop", false),
+ subElement.GetAttributeBool("onlyinsamesub", false))
{
VolumeProperty = subElement.GetAttributeIdentifier("volumeproperty", "")
};
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs
index 433243ee3..b5e745407 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs
@@ -50,6 +50,9 @@ namespace Barotrauma.Items.Components
[Serialize("FabricatorCreate", IsPropertySaveable.Yes)]
public string CreateButtonText { get; set; }
+ [Serialize("vendingmachine.outofstock", IsPropertySaveable.Yes)]
+ public string FabricationLimitReachedText { get; set; }
+
partial void InitProjSpecific()
{
//CreateGUI();
@@ -195,7 +198,7 @@ namespace Barotrauma.Items.Components
foreach (FabricationRecipe fi in fabricationRecipes.Values)
{
- var frame = new GUIFrame(new RectTransform(new Point(itemList.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null)
+ var frame = new GUIFrame(new RectTransform(new Point(itemList.Content.Rect.Width, (int)(40 * GUI.yScale)), itemList.Content.RectTransform), style: null)
{
UserData = fi,
HoverColor = Color.Gold * 0.2f,
@@ -223,6 +226,13 @@ namespace Barotrauma.Items.Components
AutoScaleVertical = true,
ToolTip = fi.TargetItem.Description
};
+
+ new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), frame.RectTransform, Anchor.BottomRight),
+ TextManager.Get(FabricationLimitReachedText), font: GUIStyle.SmallFont, textAlignment: Alignment.BottomRight)
+ {
+ UserData = nameof(FabricationLimitReachedText),
+ Visible = false
+ };
}
}
@@ -297,7 +307,8 @@ namespace Barotrauma.Items.Components
}
else
{
- sufficientSkillsText.Visible = false;
+ sufficientSkillsText.Visible = insufficientSkillsText.Visible = false;
+ sufficientSkillsText.Enabled = insufficientSkillsText.Enabled = false;
}
var requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
@@ -493,14 +504,15 @@ namespace Barotrauma.Items.Components
if (string.IsNullOrWhiteSpace(filter))
{
itemList.Content.Children.ForEach(c => c.Visible = true);
- return true;
}
-
- foreach (GUIComponent child in itemList.Content.Children)
+ else
{
- FabricationRecipe recipe = child.UserData as FabricationRecipe;
- if (recipe?.DisplayName == null) { continue; }
- child.Visible = recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase);
+ foreach (GUIComponent child in itemList.Content.Children)
+ {
+ FabricationRecipe recipe = child.UserData as FabricationRecipe;
+ if (recipe?.DisplayName == null) { continue; }
+ child.Visible = recipe.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase);
+ }
}
HideEmptyItemListCategories();
@@ -516,7 +528,10 @@ namespace Barotrauma.Items.Components
{
if (!(child.UserData is FabricationRecipe recipe))
{
- child.Visible = recipeVisible;
+ if (child.Enabled)
+ {
+ child.Visible = recipeVisible;
+ }
recipeVisible = false;
}
else
@@ -719,24 +734,26 @@ namespace Barotrauma.Items.Components
{
foreach (GUIComponent child in itemList.Content.Children)
{
- if (!(child.UserData is FabricationRecipe itemPrefab)) { continue; }
+ if (!(child.UserData is FabricationRecipe recipe)) { continue; }
- if (itemPrefab != selectedItem &&
+ if (recipe != selectedItem &&
(child.Rect.Y > itemList.Rect.Bottom || child.Rect.Bottom < itemList.Rect.Y))
{
continue;
}
- bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients, character);
- if (itemPrefab == selectedItem)
+ bool canBeFabricated = CanBeFabricated(recipe, availableIngredients, character);
+ if (recipe == selectedItem)
{
activateButton.Enabled = canBeFabricated;
}
var childContainer = child.GetChild();
-
childContainer.GetChild().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
- childContainer.GetChild().Color = itemPrefab.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
+ childContainer.GetChild().Color = recipe.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
+
+ var limitReachedText = child.FindChild(nameof(FabricationLimitReachedText));
+ limitReachedText.Visible = !canBeFabricated && fabricationLimits.TryGetValue(recipe.RecipeHash, out int amount) && amount <= 0;
}
}
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs
index a1ea10952..87289c772 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs
@@ -396,7 +396,7 @@ namespace Barotrauma.Items.Components
private bool VisibleOnItemFinder(Item it)
{
- if (it.Submarine != item.Submarine) { return false; }
+ if (!item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true)) { return false; }
if (it.NonInteractable || it.HiddenInGame) { return false; }
if (it.GetComponent() == null) { return false; }
@@ -432,10 +432,10 @@ namespace Barotrauma.Items.Components
scissorComponent = new GUIScissorComponent(new RectTransform(Vector2.One, submarineContainer.RectTransform, Anchor.Center));
miniMapContainer = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform, Anchor.Center), style: null) { CanBeFocused = false };
- ImmutableHashSet- hullPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent() != null || it.GetComponent() != null)).ToImmutableHashSet();
+ ImmutableHashSet
- hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent() != null || it.GetComponent() != null)).ToImmutableHashSet();
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
- IEnumerable
- electrialPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.GetComponent() != null);
+ IEnumerable
- electrialPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.GetComponent() != null);
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electrialPointsOfInterest, out electricalMapComponents);
Dictionary electricChildren = new Dictionary();
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs
index 0b68a66ed..4f0dfbe2c 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs
@@ -412,7 +412,7 @@ namespace Barotrauma
new GUIMessageBox(string.Empty, TextManager.Get("LockedPathTooltip"));
}
//clients aren't allowed to select the location without a permission
- else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
+ else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap) ?? false)
{
connectionHighlightState = 0.0f;
SelectedConnection = connection;
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs
index 559fe77b3..1d3c335a7 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs
@@ -271,6 +271,7 @@ namespace Barotrauma.Networking
otherClients = new List();
serverSettings = new ServerSettings(this, "Server", 0, 0, 0, false, false);
+ Voting = new Voting();
if (steamId == 0)
{
@@ -637,7 +638,7 @@ namespace Barotrauma.Networking
if (gameStarted && Screen.Selected == GameMain.GameScreen)
{
- EndVoteTickBox.Visible = serverSettings.Voting.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode);
+ EndVoteTickBox.Visible = ServerSettings.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode);
respawnManager?.Update(deltaTime);
@@ -898,13 +899,13 @@ namespace Barotrauma.Networking
GUI.SetSavingIndicatorState(save);
break;
case ServerPacketHeader.CAMPAIGN_SETUP_INFO:
- UInt16 saveCount = inc.ReadUInt16();
- List saveFiles = new List();
+ byte saveCount = inc.ReadByte();
+ List saveInfos = new List();
for (int i = 0; i < saveCount; i++)
{
- saveFiles.Add(inc.ReadString());
+ saveInfos.Add(INetSerializableStruct.Read(inc));
}
- MultiPlayerCampaign.StartCampaignSetup(saveFiles);
+ MultiPlayerCampaign.StartCampaignSetup(saveInfos);
break;
case ServerPacketHeader.PERMISSIONS:
ReadPermissions(inc);
@@ -1458,7 +1459,7 @@ namespace Barotrauma.Networking
{
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
- campaign.CampaignUI?.UpgradeStore?.RefreshAll();
+ campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
campaign.CampaignUI?.CrewManagement?.RefreshPermissions();
}
}
@@ -1666,10 +1667,7 @@ namespace Barotrauma.Networking
isOutpost = levelData.Type == LevelData.LevelType.Outpost;
}
- if (GameMain.Client?.ServerSettings?.Voting != null)
- {
- GameMain.Client.ServerSettings.Voting.ResetVotes(GameMain.Client.ConnectedClients);
- }
+ Voting?.ResetVotes(GameMain.Client.ConnectedClients);
if (loadTask != null)
{
@@ -1882,7 +1880,7 @@ namespace Barotrauma.Networking
var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.StringRepresentation == subHash);
if (matchingSub == null)
{
- matchingSub = new SubmarineInfo(Path.Combine(SubmarineInfo.SavePath, subName) + ".sub", subHash, tryLoad: false)
+ matchingSub = new SubmarineInfo(Path.Combine(SaveUtil.SubmarineDownloadFolder, subName) + ".sub", subHash, tryLoad: false)
{
SubmarineClass = (SubmarineClass)subClass
};
@@ -2086,7 +2084,7 @@ namespace Barotrauma.Networking
{
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
- campaign.CampaignUI?.UpgradeStore?.RefreshAll();
+ campaign.CampaignUI?.UpgradeStore?.RequestRefresh();
campaign.CampaignUI?.CrewManagement?.RefreshPermissions();
}
}
@@ -2208,8 +2206,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
serverSettings.VoiceChatEnabled = voiceChatEnabled;
- serverSettings.Voting.AllowSubVoting = allowSubVoting;
- serverSettings.Voting.AllowModeVoting = allowModeVoting;
+ serverSettings.AllowSubVoting = allowSubVoting;
+ serverSettings.AllowModeVoting = allowModeVoting;
if (clientPeer is SteamP2POwnerPeer)
{
@@ -2240,7 +2238,7 @@ namespace Barotrauma.Networking
ChatMessage.ClientRead(inc);
break;
case ServerNetObject.VOTE:
- serverSettings.Voting.ClientRead(inc);
+ Voting.ClientRead(inc);
break;
}
}
@@ -2826,12 +2824,12 @@ namespace Barotrauma.Networking
public void Vote(VoteType voteType, object data)
{
- if (clientPeer == null) return;
+ if (clientPeer == null) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.UPDATE_LOBBY);
msg.Write((byte)ClientNetObject.VOTE);
- serverSettings.Voting.ClientWrite(msg, voteType, data);
+ Voting.ClientWrite(msg, voteType, data);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer.Send(msg, DeliveryMethod.Reliable);
@@ -2847,19 +2845,27 @@ namespace Barotrauma.Networking
#region Submarine Change Voting
public void InitiateSubmarineChange(SubmarineInfo sub, VoteType voteType)
{
- if (sub == null) return;
- if (serverSettings.Voting.VoteRunning)
- {
- new GUIMessageBox(TextManager.Get("unabletoinitiateavoteheader"), TextManager.Get("votealreadyactivetext"));
- return;
- }
+ if (sub == null) { return; }
Vote(voteType, sub);
}
public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, float timeOut)
{
- if (info == null || votingInterface != null) return;
- votingInterface = new VotingInterface(starter, info, type, timeOut);
+ if (info == null || votingInterface != null) { return; }
+ votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, timeOut);
+ }
+ #endregion
+
+ #region Money Transfer Voting
+ public void ShowMoneyTransferVoteInterface(Client starter, Client from, int amount, Client to, float timeOut)
+ {
+ if (votingInterface != null) { return; }
+ if (from == null && to == null)
+ {
+ DebugConsole.ThrowError("Tried to initiate a vote for transferring from null to null!");
+ return;
+ }
+ votingInterface = VotingInterface.CreateMoneyTransferVotingInterface(starter, from, to, amount, timeOut);
}
#endregion
@@ -3103,7 +3109,7 @@ namespace Barotrauma.Networking
{
if (!gameStarted) return false;
- if (!serverSettings.Voting.AllowEndVoting || !HasSpawned)
+ if (!serverSettings.AllowEndVoting || !HasSpawned)
{
tickBox.Visible = false;
return false;
@@ -3322,15 +3328,17 @@ namespace Barotrauma.Networking
inGameHUD.DrawManually(spriteBatch);
- if (EndVoteCount > 0)
+ int endVoteCount = Voting.GetVoteCountYes(VoteType.EndRound);
+ int endVoteMax = Voting.GetVoteCountMax(VoteType.EndRound);
+ if (endVoteCount > 0)
{
if (EndVoteTickBox.Visible)
{
- EndVoteTickBox.Text = $"{endRoundVoteText} {EndVoteCount}/{EndVoteMax}";
+ EndVoteTickBox.Text = $"{endRoundVoteText} {endVoteCount}/{endVoteMax}";
}
else
{
- LocalizedString endVoteText = TextManager.GetWithVariables("EndRoundVotes", ("[votes]", EndVoteCount.ToString()), ("[max]", EndVoteMax.ToString()));
+ LocalizedString endVoteText = TextManager.GetWithVariables("EndRoundVotes", ("[votes]", endVoteCount.ToString()), ("[max]", endVoteMax.ToString()));
GUI.DrawString(spriteBatch, EndVoteTickBox.Rect.Center.ToVector2() - GUIStyle.SmallFont.MeasureString(endVoteText) / 2,
endVoteText.Value,
Color.White,
@@ -3498,7 +3506,7 @@ namespace Barotrauma.Networking
OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.KickPlayer(client); return false; }
};
}
- else if (serverSettings.Voting.AllowVoteKick && client.AllowKicking)
+ else if (serverSettings.AllowVoteKick && client.AllowKicking)
{
var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.9f), buttonContainer.RectTransform),
TextManager.Get("VoteToKick"), style: "GUIButtonSmall")
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs
index 23458baca..e088e3b15 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs
@@ -2,56 +2,42 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
-using Barotrauma.Extensions;
namespace Barotrauma
{
partial class Voting
{
- public bool AllowSubVoting
- {
- get { return allowSubVoting; }
- set
- {
- if (value == allowSubVoting) return;
- allowSubVoting = value;
- GameMain.NetLobbyScreen.SubList.Enabled = value ||
- (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectSub));
- var subVotesLabel = GameMain.NetLobbyScreen.Frame.FindChild("subvotes", true) as GUITextBlock;
- subVotesLabel.Visible = value;
- var subVisButton = GameMain.NetLobbyScreen.SubVisibilityButton;
- subVisButton.RectTransform.AbsoluteOffset
- = new Point(value ? (int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
+ private readonly Dictionary
+ voteCountYes = new Dictionary(),
+ voteCountNo = new Dictionary(),
+ voteCountMax = new Dictionary();
- UpdateVoteTexts(null, VoteType.Sub);
- GameMain.NetLobbyScreen.SubList.Deselect();
- }
+ public int GetVoteCountYes(VoteType voteType)
+ {
+ voteCountYes.TryGetValue(voteType, out int value);
+ return value;
}
- public bool AllowModeVoting
+ public int GetVoteCountNo(VoteType voteType)
{
- get { return allowModeVoting; }
- set
- {
- if (value == allowModeVoting) return;
- allowModeVoting = value;
- GameMain.NetLobbyScreen.ModeList.Enabled =
- value ||
- (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectMode));
-
- GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
-
- // Disable modes that cannot be voted on
- foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
- {
- if (guiComponent is GUIFrame frame)
- {
- frame.CanBeFocused = !allowModeVoting || ((GameModePreset) frame.UserData).Votable;
- }
- }
-
- UpdateVoteTexts(null, VoteType.Mode);
- GameMain.NetLobbyScreen.ModeList.Deselect();
- }
+ voteCountNo.TryGetValue(voteType, out int value);
+ return value;
+ }
+ public int GetVoteCountMax(VoteType voteType)
+ {
+ voteCountMax.TryGetValue(voteType, out int value);
+ return value;
+ }
+ public void SetVoteCountYes(VoteType voteType, int value)
+ {
+ voteCountYes[voteType] = value;
+ }
+ public void SetVoteCountNo(VoteType voteType, int value)
+ {
+ voteCountNo[voteType] = value;
+ }
+ public void SetVoteCountMax(VoteType voteType, int value)
+ {
+ voteCountMax[voteType] = value;
}
public void UpdateVoteTexts(List clients, VoteType voteType)
@@ -139,17 +125,15 @@ namespace Barotrauma
msg.Write(votedClient.ID);
break;
case VoteType.StartRound:
- if (!(data is bool)) return;
+ if (!(data is bool)) { return; }
msg.Write((bool)data);
break;
-
case VoteType.PurchaseAndSwitchSub:
case VoteType.PurchaseSub:
case VoteType.SwitchSub:
- if (!VoteRunning)
- {
- SubmarineInfo voteSub = data as SubmarineInfo;
- if (voteSub == null) return;
+ if (data is SubmarineInfo voteSub)
+ {
+ //initiate sub vote
msg.Write(true);
msg.Write(voteSub.Name);
}
@@ -159,7 +143,11 @@ namespace Barotrauma
msg.Write(false);
msg.Write((int)data);
}
-
+ break;
+ case VoteType.TransferMoney:
+ if (!(data is int)) { return; }
+ msg.Write(false); //not initiating a vote
+ msg.Write((int)data);
break;
}
@@ -168,8 +156,8 @@ namespace Barotrauma
public void ClientRead(IReadMessage inc)
{
- AllowSubVoting = inc.ReadBoolean();
- if (allowSubVoting)
+ GameMain.Client.ServerSettings.AllowSubVoting = inc.ReadBoolean();
+ if (GameMain.Client.ServerSettings.AllowSubVoting)
{
UpdateVoteTexts(null, VoteType.Sub);
int votableCount = inc.ReadByte();
@@ -186,8 +174,8 @@ namespace Barotrauma
SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes);
}
}
- AllowModeVoting = inc.ReadBoolean();
- if (allowModeVoting)
+ GameMain.Client.ServerSettings.AllowModeVoting = inc.ReadBoolean();
+ if (GameMain.Client.ServerSettings.AllowModeVoting)
{
UpdateVoteTexts(null, VoteType.Mode);
int votableCount = inc.ReadByte();
@@ -199,135 +187,136 @@ namespace Barotrauma
SetVoteText(GameMain.NetLobbyScreen.ModeList, mode, votes);
}
}
- AllowEndVoting = inc.ReadBoolean();
- if (AllowEndVoting)
+ GameMain.Client.ServerSettings.AllowEndVoting = inc.ReadBoolean();
+ if (GameMain.Client.ServerSettings.AllowEndVoting)
{
- GameMain.NetworkMember.EndVoteCount = inc.ReadByte();
- GameMain.NetworkMember.EndVoteMax = inc.ReadByte();
+ SetVoteCountYes(VoteType.EndRound, inc.ReadByte());
+ SetVoteCountMax(VoteType.EndRound, inc.ReadByte());
}
- AllowVoteKick = inc.ReadBoolean();
+ GameMain.Client.ServerSettings.AllowVoteKick = inc.ReadBoolean();
- byte subVoteStateByte = inc.ReadByte();
- VoteState subVoteState = VoteState.None;
- try
- {
- subVoteState = (VoteState)subVoteStateByte;
- }
+ byte activeVoteStateByte = inc.ReadByte();
+
+ VoteState activeVoteState = VoteState.None;
+ try { activeVoteState = (VoteState)activeVoteStateByte; }
catch (System.Exception e)
{
- DebugConsole.ThrowError("Failed to cast vote type \"" + subVoteStateByte + "\"", e);
+ DebugConsole.ThrowError("Failed to cast vote type \"" + activeVoteStateByte + "\"", e);
}
- if (subVoteState != VoteState.None)
+ if (activeVoteState != VoteState.None)
{
byte voteTypeByte = inc.ReadByte();
VoteType voteType = VoteType.Unknown;
-
- try
- {
- voteType = (VoteType)voteTypeByte;
- }
+ try { voteType = (VoteType)voteTypeByte; }
catch (System.Exception e)
{
DebugConsole.ThrowError("Failed to cast vote type \"" + voteTypeByte + "\"", e);
}
- if (voteType != VoteType.Unknown)
+ byte yesClientCount = inc.ReadByte();
+ for (int i = 0; i < yesClientCount; i++)
{
- byte yesClientCount = inc.ReadByte();
- for (int i = 0; i < yesClientCount; i++)
- {
- byte clientID = inc.ReadByte();
- var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == clientID);
- matchingClient?.SetVote(voteType, 2);
- }
-
- byte noClientCount = inc.ReadByte();
- for (int i = 0; i < noClientCount; i++)
- {
- byte clientID = inc.ReadByte();
- var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == clientID);
- matchingClient?.SetVote(voteType, 1);
- }
-
- GameMain.NetworkMember.SubmarineVoteYesCount = yesClientCount;
- GameMain.NetworkMember.SubmarineVoteNoCount = noClientCount;
- GameMain.NetworkMember.SubmarineVoteMax = inc.ReadByte();
-
- switch (subVoteState)
- {
- case VoteState.Started:
- Client myClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == GameMain.Client.ID);
- if (!myClient.InGame)
- {
- VoteRunning = true;
- return;
- }
-
- string subName1 = inc.ReadString();
- SubmarineInfo info = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1);
-
- if (info == null)
- {
- DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
- return;
- }
-
- VoteRunning = true;
- byte starterID = inc.ReadByte();
- Client starterClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == starterID);
- float timeOut = inc.ReadByte();
- GameMain.Client.ShowSubmarineChangeVoteInterface(starterClient, info, voteType, timeOut);
- break;
- case VoteState.Running:
- // Nothing specific
- break;
- case VoteState.Passed:
- case VoteState.Failed:
- VoteRunning = false;
-
- bool passed = inc.ReadBoolean();
- string subName2 = inc.ReadString();
- SubmarineInfo subInfo = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
-
- if (subInfo == null)
- {
- DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
- return;
- }
-
- if (GameMain.Client.VotingInterface != null)
- {
- GameMain.Client.VotingInterface.EndVote(passed, yesClientCount, noClientCount);
- }
- else if (GameMain.Client.ConnectedClients.Count > 1)
- {
- GameMain.NetworkMember.AddChatMessage(VotingInterface.GetSubmarineVoteResultMessage(subInfo, voteType, yesClientCount.ToString(), noClientCount.ToString(), passed).Value, ChatMessageType.Server);
- }
-
- if (passed)
- {
- int deliveryFee = inc.ReadInt16();
- switch (voteType)
- {
- case VoteType.PurchaseAndSwitchSub:
- GameMain.GameSession.PurchaseSubmarine(subInfo);
- GameMain.GameSession.SwitchSubmarine(subInfo, 0);
- break;
- case VoteType.PurchaseSub:
- GameMain.GameSession.PurchaseSubmarine(subInfo);
- break;
- case VoteType.SwitchSub:
- GameMain.GameSession.SwitchSubmarine(subInfo, deliveryFee);
- break;
- }
-
- SubmarineSelection.ContentRefreshRequired = true;
- }
- break;
- }
+ byte clientID = inc.ReadByte();
+ var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == clientID);
+ matchingClient?.SetVote(voteType, 2);
}
- }
+
+ byte noClientCount = inc.ReadByte();
+ for (int i = 0; i < noClientCount; i++)
+ {
+ byte clientID = inc.ReadByte();
+ var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == clientID);
+ matchingClient?.SetVote(voteType, 1);
+ }
+ byte maxClientCount = inc.ReadByte();
+
+ SetVoteCountYes(voteType, yesClientCount);
+ SetVoteCountNo(voteType, noClientCount);
+ SetVoteCountMax(voteType, maxClientCount);
+
+ switch (activeVoteState)
+ {
+ case VoteState.Started:
+ byte starterID = inc.ReadByte();
+ Client starterClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == starterID);
+ float timeOut = inc.ReadByte();
+
+ Client myClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == GameMain.Client.ID);
+ if (!myClient.InGame) { return; }
+
+ switch (voteType)
+ {
+ case VoteType.PurchaseSub:
+ case VoteType.PurchaseAndSwitchSub:
+ case VoteType.SwitchSub:
+ string subName1 = inc.ReadString();
+ SubmarineInfo info = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1);
+ if (info == null)
+ {
+ DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
+ return;
+ }
+ GameMain.Client.ShowSubmarineChangeVoteInterface(starterClient, info, voteType, timeOut);
+ break;
+ case VoteType.TransferMoney:
+ byte fromClientId = inc.ReadByte();
+ byte toClientId = inc.ReadByte();
+ int transferAmount = inc.ReadInt32();
+
+ Client fromClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == fromClientId);
+ Client toClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == toClientId);
+ GameMain.Client.ShowMoneyTransferVoteInterface(starterClient, fromClient, transferAmount, toClient, timeOut);
+ break;
+ }
+ break;
+ case VoteState.Running:
+ // Nothing specific
+ break;
+ case VoteState.Passed:
+ case VoteState.Failed:
+ bool passed = inc.ReadBoolean();
+
+ SubmarineInfo subInfo = null;
+ switch (voteType)
+ {
+ case VoteType.PurchaseSub:
+ case VoteType.PurchaseAndSwitchSub:
+ case VoteType.SwitchSub:
+ string subName2 = inc.ReadString();
+ subInfo = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
+ if (subInfo == null)
+ {
+ DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
+ return;
+ }
+ break;
+ }
+
+ GameMain.Client.VotingInterface?.EndVote(passed, yesClientCount, noClientCount);
+
+ if (passed && subInfo != null)
+ {
+ int deliveryFee = inc.ReadInt16();
+ switch (voteType)
+ {
+ case VoteType.PurchaseAndSwitchSub:
+ GameMain.GameSession.PurchaseSubmarine(subInfo);
+ GameMain.GameSession.SwitchSubmarine(subInfo, 0);
+ break;
+ case VoteType.PurchaseSub:
+ GameMain.GameSession.PurchaseSubmarine(subInfo);
+ break;
+ case VoteType.SwitchSub:
+ GameMain.GameSession.SwitchSubmarine(subInfo, deliveryFee);
+ break;
+ }
+
+ SubmarineSelection.ContentRefreshRequired = true;
+ }
+ break;
+ }
+ }
GameMain.NetworkMember.ConnectedClients.ForEach(c => c.SetVote(VoteType.StartRound, false));
byte readyClientCount = inc.ReadByte();
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs
index ff0f4ffc2..c7cd94394 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/CampaignSetupUI.cs
@@ -1,5 +1,8 @@
+using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
namespace Barotrauma
{
@@ -44,5 +47,60 @@ namespace Barotrauma
this.newGameContainer = newGameContainer;
this.loadGameContainer = loadGameContainer;
}
+
+ protected List prevSaveFiles;
+ protected GUIComponent CreateSaveElement(CampaignMode.SaveInfo saveInfo)
+ {
+ if (string.IsNullOrEmpty(saveInfo.FilePath))
+ {
+ DebugConsole.AddWarning("Error when updating campaign load menu: path to a save file was empty.\n" + Environment.StackTrace);
+ return null;
+ }
+
+ var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement")
+ {
+ UserData = saveInfo.FilePath
+ };
+
+ var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), Path.GetFileNameWithoutExtension(saveInfo.FilePath))
+ {
+ CanBeFocused = false
+ };
+
+ if (saveInfo.EnabledContentPackageNames != null && saveInfo.EnabledContentPackageNames.Any())
+ {
+ if (!GameSession.IsCompatibleWithEnabledContentPackages(saveInfo.EnabledContentPackageNames, out LocalizedString errorMsg))
+ {
+ nameText.TextColor = GUIStyle.Red;
+ saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning"));
+ }
+ }
+
+ prevSaveFiles ??= new List();
+ prevSaveFiles.Add(saveInfo);
+
+ new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
+ text: saveInfo.SubmarineName, font: GUIStyle.SmallFont)
+ {
+ CanBeFocused = false,
+ UserData = saveInfo.FilePath
+ };
+
+
+ string saveTimeStr = string.Empty;
+ if (saveInfo.SaveTime > 0)
+ {
+ DateTime time = ToolBox.Epoch.ToDateTime(saveInfo.SaveTime);
+ saveTimeStr = time.ToString();
+ }
+ new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
+ text: saveTimeStr, textAlignment: Alignment.Right, font: GUIStyle.SmallFont)
+ {
+ CanBeFocused = false,
+ UserData = saveInfo.FilePath
+ };
+
+ return saveFrame;
+ }
}
}
\ No newline at end of file
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs
index e93563192..97e08e561 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/MultiPlayerCampaignSetupUI.cs
@@ -12,7 +12,7 @@ namespace Barotrauma
{
private GUIButton deleteMpSaveButton;
- public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable saveFiles = null)
+ public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, List saveFiles = null)
: base(newGameContainer, loadGameContainer)
{
var verticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: false)
@@ -219,8 +219,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
- private List prevSaveFiles;
- public void UpdateLoadMenu(IEnumerable saveFiles = null)
+ public void UpdateLoadMenu(IEnumerable saveFiles = null)
{
prevSaveFiles?.Clear();
prevSaveFiles = null;
@@ -242,73 +241,9 @@ namespace Barotrauma
OnSelected = SelectSaveFile
};
- foreach (string saveFile in saveFiles)
+ foreach (CampaignMode.SaveInfo saveInfo in saveFiles)
{
- if (string.IsNullOrEmpty(saveFile))
- {
- DebugConsole.AddWarning("Error when updating campaign load menu: path to a save file was empty.\n" + Environment.StackTrace);
- continue;
- }
-
- string fileName = saveFile;
- string subName = "";
- string saveTime = "";
- string contentPackageStr = "";
- var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement")
- {
- UserData = saveFile
- };
-
- var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), "")
- {
- CanBeFocused = false
- };
-
- bool isCompatible = true;
- prevSaveFiles ??= new List();
-
- prevSaveFiles?.Add(saveFile);
- string[] splitSaveFile = saveFile.Split(';');
- saveFrame.UserData = splitSaveFile[0];
- fileName = Path.GetFileNameWithoutExtension(splitSaveFile[0]);
- nameText.Text = fileName;
- if (splitSaveFile.Length > 1) { subName = splitSaveFile[1]; }
- if (splitSaveFile.Length > 2) { saveTime = splitSaveFile[2]; }
- if (splitSaveFile.Length > 3) { contentPackageStr = splitSaveFile[3]; }
-
- if (!string.IsNullOrEmpty(saveTime) && long.TryParse(saveTime, out long unixTime))
- {
- DateTime time = ToolBox.Epoch.ToDateTime(unixTime);
- saveTime = time.ToString();
- }
- if (!string.IsNullOrEmpty(contentPackageStr))
- {
- List contentPackagePaths = contentPackageStr.Split('|').ToList();
- if (!GameSession.IsCompatibleWithEnabledContentPackages(contentPackagePaths, out LocalizedString errorMsg))
- {
- nameText.TextColor = GUIStyle.Red;
- saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning"));
- }
- }
- if (!isCompatible)
- {
- nameText.TextColor = GUIStyle.Red;
- saveFrame.ToolTip = TextManager.Get("campaignmode.incompatiblesave");
- }
-
- new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
- text: subName, font: GUIStyle.SmallFont)
- {
- CanBeFocused = false,
- UserData = fileName
- };
-
- new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
- text: saveTime, textAlignment: Alignment.Right, font: GUIStyle.SmallFont)
- {
- CanBeFocused = false,
- UserData = fileName
- };
+ CreateSaveElement(saveInfo);
}
saveList.Content.RectTransform.SortChildren((c1, c2) =>
@@ -380,7 +315,7 @@ namespace Barotrauma
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveFile);
- prevSaveFiles?.RemoveAll(s => s.StartsWith(saveFile));
+ prevSaveFiles?.RemoveAll(s => s.FilePath == saveFile);
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs
index 9c62c6816..870827d91 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI/SinglePlayerCampaignSetupUI.cs
@@ -21,7 +21,7 @@ namespace Barotrauma
private GUIButton nextButton;
private GUILayoutGroup characterInfoColumns;
- public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable submarines, IEnumerable saveFiles = null)
+ public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable submarines, IEnumerable saveFiles = null)
: base(newGameContainer, loadGameContainer)
{
UpdateNewGameMenu(submarines);
@@ -606,8 +606,7 @@ namespace Barotrauma
}
}
- private List prevSaveFiles;
- public void UpdateLoadMenu(IEnumerable saveFiles = null)
+ public void UpdateLoadMenu(IEnumerable saveFiles = null)
{
prevSaveFiles?.Clear();
prevSaveFiles = null;
@@ -647,32 +646,16 @@ namespace Barotrauma
}
};
- foreach (string saveFile in saveFiles)
+ foreach (var saveInfo in saveFiles)
{
- string fileName = saveFile;
- string subName = "";
- string saveTime = "";
- string contentPackageStr = "";
- var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement")
- {
- UserData = saveFile
- };
-
- var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), "")
- {
- CanBeFocused = false
- };
-
- bool isCompatible = true;
- prevSaveFiles ??= new List();
-
- nameText.Text = Path.GetFileNameWithoutExtension(saveFile);
- XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile);
-
+ var saveFrame = CreateSaveElement(saveInfo);
+ if (saveFrame == null) { continue; }
+
+ XDocument doc = SaveUtil.LoadGameSessionDoc(saveInfo.FilePath);
if (doc?.Root == null)
{
- DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted.");
- nameText.TextColor = GUIStyle.Red;
+ DebugConsole.ThrowError("Error loading save file \"" + saveInfo.FilePath + "\". The file may be corrupted.");
+ saveFrame.GetChild().TextColor = GUIStyle.Red;
continue;
}
if (doc.Root.GetChildElement("multiplayercampaign") != null)
@@ -681,44 +664,11 @@ namespace Barotrauma
saveList.Content.RemoveChild(saveFrame);
continue;
}
- subName = doc.Root.GetAttributeString("submarine", "");
- saveTime = doc.Root.GetAttributeString("savetime", "");
- isCompatible = SaveUtil.IsSaveFileCompatible(doc);
- contentPackageStr = doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", "");
- prevSaveFiles?.Add(saveFile);
- if (!string.IsNullOrEmpty(saveTime) && long.TryParse(saveTime, out long unixTime))
+ if (!SaveUtil.IsSaveFileCompatible(doc))
{
- DateTime time = ToolBox.Epoch.ToDateTime(unixTime);
- saveTime = time.ToString();
- }
- if (!string.IsNullOrEmpty(contentPackageStr))
- {
- List contentPackagePaths = contentPackageStr.Split('|').ToList();
- if (!GameSession.IsCompatibleWithEnabledContentPackages(contentPackagePaths, out LocalizedString errorMsg))
- {
- nameText.TextColor = GUIStyle.Red;
- saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning"));
- }
- }
- if (!isCompatible)
- {
- nameText.TextColor = GUIStyle.Red;
+ saveFrame.GetChild().TextColor = GUIStyle.Red;
saveFrame.ToolTip = TextManager.Get("campaignmode.incompatiblesave");
}
-
- new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft),
- text: subName, font: GUIStyle.SmallFont)
- {
- CanBeFocused = false,
- UserData = fileName
- };
-
- new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform),
- text: saveTime, textAlignment: Alignment.Right, font: GUIStyle.SmallFont)
- {
- CanBeFocused = false,
- UserData = fileName
- };
}
saveList.Content.RectTransform.SortChildren((c1, c2) =>
@@ -830,7 +780,7 @@ namespace Barotrauma
EventEditorScreen.AskForConfirmation(header, body, () =>
{
SaveUtil.DeleteSave(saveFile);
- prevSaveFiles?.RemoveAll(s => s.StartsWith(saveFile));
+ prevSaveFiles?.RemoveAll(s => s.FilePath == saveFile);
UpdateLoadMenu(prevSaveFiles.ToList());
return true;
});
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs
index efbb7b0cc..c1864e3cc 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs
@@ -1,4 +1,5 @@
using Barotrauma.Extensions;
+using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -332,7 +333,7 @@ namespace Barotrauma
foreach (GUITickBox tickBox in missionTickBoxes)
{
bool disable = hasMaxMissions && !tickBox.Selected;
- tickBox.Enabled = Campaign.AllowedToManageCampaign() && !disable;
+ tickBox.Enabled = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap) && !disable;
tickBox.Box.DisabledColor = disable ? tickBox.Box.Color * 0.5f : tickBox.Box.Color * 0.8f;
foreach (GUIComponent child in tickBox.Parent.Parent.Children)
{
@@ -480,7 +481,7 @@ namespace Barotrauma
if (GUI.MouseOn == tickBox) { return false; }
if (tickBox != null)
{
- if (Campaign.AllowedToManageCampaign() && tickBox.Enabled)
+ if (Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap) && tickBox.Enabled)
{
tickBox.Selected = !tickBox.Selected;
}
@@ -521,10 +522,10 @@ namespace Barotrauma
};
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
tickBox.RectTransform.IsFixedSize = true;
- tickBox.Enabled = Campaign.AllowedToManageCampaign();
+ tickBox.Enabled = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap);
tickBox.OnSelected += (GUITickBox tb) =>
{
- if (!Campaign.AllowedToManageCampaign()) { return false; }
+ if (!Campaign.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }
if (tb.Selected)
{
@@ -544,7 +545,7 @@ namespace Barotrauma
UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
- Campaign.AllowedToManageCampaign())
+ Campaign.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
{
GameMain.Client?.SendCampaignState();
}
@@ -665,7 +666,7 @@ namespace Barotrauma
return true;
},
Enabled = true,
- Visible = Campaign.AllowedToEndRound()
+ Visible = Campaign.AllowedToManageCampaign(ClientPermissions.ManageMap)
};
buttonArea.RectTransform.MinSize = new Point(0, StartButton.RectTransform.MinSize.Y);
@@ -702,12 +703,10 @@ namespace Barotrauma
{
case CampaignMode.InteractionType.Repair:
repairHullsButton.Enabled =
- (Campaign.PurchasedHullRepairs || Campaign.Wallet.CanAfford(CampaignMode.HullRepairCost)) &&
- Campaign.AllowedToManageCampaign();
+ (Campaign.PurchasedHullRepairs || Campaign.Wallet.CanAfford(CampaignMode.HullRepairCost));
repairHullsButton.GetChild().Selected = Campaign.PurchasedHullRepairs;
repairItemsButton.Enabled =
- (Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost)) &&
- Campaign.AllowedToManageCampaign();
+ (Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost));
repairItemsButton.GetChild().Selected = Campaign.PurchasedItemRepairs;
if (GameMain.GameSession?.SubmarineInfo == null || !GameMain.GameSession.SubmarineInfo.SubsLeftBehind)
@@ -718,8 +717,7 @@ namespace Barotrauma
else
{
replaceShuttlesButton.Enabled =
- (Campaign.PurchasedLostShuttles || Campaign.Wallet.CanAfford(CampaignMode.ShuttleReplaceCost)) &&
- Campaign.AllowedToManageCampaign();
+ (Campaign.PurchasedLostShuttles || Campaign.Wallet.CanAfford(CampaignMode.ShuttleReplaceCost));
replaceShuttlesButton.GetChild().Selected = Campaign.PurchasedLostShuttles;
}
break;
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/Wizard.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/Wizard.cs
index 8fb996949..d0c67ae6a 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/Wizard.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/Wizard.cs
@@ -387,11 +387,14 @@ namespace Barotrauma.CharacterEditor
contentPackageDropDown.Flash();
return false;
}
- if (!File.Exists(TexturePath))
+ if (SourceCharacter?.SpeciesName != CharacterPrefab.HumanSpeciesName)
{
- GUI.AddMessage(GetCharacterEditorTranslation("TextureDoesNotExist"), GUIStyle.Red);
- texturePathElement.Flash(GUIStyle.Red);
- return false;
+ if (!File.Exists(TexturePath))
+ {
+ GUI.AddMessage(GetCharacterEditorTranslation("TextureDoesNotExist"), GUIStyle.Red);
+ texturePathElement.Flash(GUIStyle.Red);
+ return false;
+ }
}
var path = Path.GetFileName(TexturePath);
if (!path.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs
index be295b3ed..7108479ce 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/EditorScreen.cs
@@ -10,11 +10,13 @@ namespace Barotrauma
public override sealed void Deselect()
{
DeselectEditorSpecific();
+#if !DEBUG
//reset cheats the player might have used in the editor
GameMain.LightManager.LightingEnabled = true;
GameMain.LightManager.LosEnabled = true;
Hull.EditFire = false;
Hull.EditWater = false;
+#endif
}
protected virtual void DeselectEditorSpecific() { }
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs
index 7fb77b452..f8468ea3c 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs
@@ -1006,13 +1006,12 @@ namespace Barotrauma
spriteBatch.End();
}
- private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed, CampaignSettings settings)
+ private void StartGame(SubmarineInfo selectedSub, string savePath, string mapSeed, CampaignSettings settings)
{
- if (string.IsNullOrEmpty(saveName)) return;
+ if (string.IsNullOrEmpty(savePath)) { return; }
var existingSaveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
-
- if (existingSaveFiles.Any(s => s == saveName))
+ if (existingSaveFiles.Any(s => s.FilePath == savePath))
{
new GUIMessageBox(TextManager.Get("SaveNameInUseHeader"), TextManager.Get("SaveNameInUseText"));
return;
@@ -1045,7 +1044,7 @@ namespace Barotrauma
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
- GameMain.GameSession = new GameSession(selectedSub, saveName, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
+ GameMain.GameSession = new GameSession(selectedSub, savePath, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
GameMain.GameSession.CrewManager.CharacterInfos.Clear();
foreach (var characterInfo in campaignSetupUI.CharacterMenus.Select(m => m.CharacterInfo))
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs
index af54e3654..a4cf42674 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs
@@ -1299,7 +1299,7 @@ namespace Barotrauma
if (GameMain.Client != null)
{
- GameMain.Client.ServerSettings.Voting.ResetVotes(GameMain.Client.ConnectedClients);
+ GameMain.Client.Voting.ResetVotes(GameMain.Client.ConnectedClients);
spectateButton.OnClicked = GameMain.Client.SpectateClicked;
ReadyToStartBox.OnSelected = GameMain.Client.SetReadyToStart;
}
@@ -1307,9 +1307,6 @@ namespace Barotrauma
roundControlsHolder.Children.ForEach(c => c.IgnoreLayoutGroups = !c.Visible);
roundControlsHolder.Recalculate();
- GameMain.NetworkMember.EndVoteCount = 0;
- GameMain.NetworkMember.EndVoteMax = 1;
-
base.Select();
}
@@ -1348,9 +1345,9 @@ namespace Barotrauma
ServerName.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
ServerMessage.Readonly = !GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings) && !GameMain.Client.GameStarted;
- SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.Voting.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub));
+ SubList.Enabled = !CampaignFrame.Visible && (GameMain.Client.ServerSettings.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub));
ShuttleList.Enabled = ShuttleList.ButtonEnabled = GameMain.Client.HasPermission(ClientPermissions.SelectSub) && !GameMain.Client.GameStarted;
- ModeList.Enabled = GameMain.Client.ServerSettings.Voting.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode);
+ ModeList.Enabled = GameMain.Client.ServerSettings.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode);
LogButtons.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
GameMain.Client.ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog);
roundControlsHolder.Children.ForEach(c => c.IgnoreLayoutGroups = !c.Visible);
@@ -1889,7 +1886,7 @@ namespace Barotrauma
}
else
{
- var classText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), parent.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
+ new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), parent.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(GUI.IntScale(20), 0) },
TextManager.Get($"submarineclass.{sub.SubmarineClass}"), textAlignment: Alignment.CenterRight, font: GUIStyle.SmallFont)
{
UserData = "classtext",
@@ -1907,7 +1904,7 @@ namespace Barotrauma
VoteType voteType;
if (component.Parent == GameMain.NetLobbyScreen.SubList.Content)
{
- if (!GameMain.Client.ServerSettings.Voting.AllowSubVoting)
+ if (!GameMain.Client.ServerSettings.AllowSubVoting)
{
var selectedSub = component.UserData as SubmarineInfo;
if (!selectedSub.RequiredContentPackagesInstalled)
@@ -1942,7 +1939,7 @@ namespace Barotrauma
}
else if (component.Parent == GameMain.NetLobbyScreen.ModeList.Content)
{
- if (!GameMain.Client.ServerSettings.Voting.AllowModeVoting)
+ if (!GameMain.Client.ServerSettings.AllowModeVoting)
{
if (GameMain.Client.HasPermission(ClientPermissions.SelectMode))
{
@@ -2384,6 +2381,7 @@ namespace Barotrauma
return true;
}
};
+ permissionTick.ToolTip = permissionTick.TextBlock.ToolTip = TextManager.Get("ClientPermission." + permission + ".description");
}
var listBoxContainerRight = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform))
@@ -2478,7 +2476,7 @@ namespace Barotrauma
if (GameMain.Client != null && GameMain.Client.ConnectedClients.Contains(selectedClient))
{
- if (GameMain.Client.ServerSettings.Voting.AllowVoteKick &&
+ if (GameMain.Client.ServerSettings.AllowVoteKick &&
selectedClient != null && selectedClient.AllowKicking)
{
var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.34f, 1.0f), buttonAreaLower.RectTransform),
@@ -3564,22 +3562,7 @@ namespace Barotrauma
if (GameMain.Client.ServerSettings.AllowFileTransfers)
{
- errorMsg += TextManager.Get("DownloadSubQuestion");
-
- var requestFileBox = new GUIMessageBox(TextManager.Get("DownloadSubLabel"), errorMsg,
- new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
- {
- UserData = "request" + subName
- };
- requestFileBox.Buttons[0].UserData = new string[] { subName, md5Hash };
- requestFileBox.Buttons[0].OnClicked += requestFileBox.Close;
- requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) =>
- {
- string[] fileInfo = (string[])userdata;
- GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]);
- return true;
- };
- requestFileBox.Buttons[1].OnClicked += requestFileBox.Close;
+ GameMain.Client?.RequestFile(FileTransferType.Submarine, subName, md5Hash);
}
else
{
@@ -3596,7 +3579,7 @@ namespace Barotrauma
public bool CheckIfCampaignSubMatches(SubmarineInfo serverSubmarine, SubmarineDeliveryData deliveryData)
{
- if (GameMain.Client == null) return false;
+ if (GameMain.Client == null) { return false; }
//already downloading the selected sub file
if (GameMain.Client.FileReceiver.ActiveTransfers.Any(t => t.FileName == serverSubmarine.Name + ".sub"))
@@ -3610,59 +3593,19 @@ namespace Barotrauma
return true;
}
- purchasableSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == serverSubmarine.Name);
+ FailedSubInfo fileInfo = new FailedSubInfo(serverSubmarine.Name, serverSubmarine.MD5Hash.StringRepresentation);
- LocalizedString errorMsg = "";
- if (purchasableSub == null)
+ switch (deliveryData)
{
- errorMsg = TextManager.GetWithVariable("SubNotFoundError", "[subname]", serverSubmarine.Name) + " ";
- }
- else if (purchasableSub.MD5Hash?.StringRepresentation == null)
- {
- errorMsg = TextManager.GetWithVariable("SubLoadError", "[subname]", serverSubmarine.Name) + " ";
- /*GUITextBlock textBlock = subList.Content.GetChildByUserData(sub)?.GetChild();
- if (textBlock != null) { textBlock.TextColor = GUIStyle.Red; }*/
- }
- else
- {
- errorMsg = TextManager.GetWithVariables("SubDoesntMatchError",
- ("[subname]", purchasableSub.Name),
- ("[myhash]", purchasableSub.MD5Hash.ShortRepresentation),
- ("[serverhash]", Md5Hash.GetShortHash(serverSubmarine.MD5Hash.StringRepresentation))) + " ";
- }
-
- errorMsg += TextManager.Get("DownloadSubQuestion");
-
- //already showing a message about the same sub
- if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "request" + serverSubmarine.Name))
- {
- return false;
- }
-
- var requestFileBox = new GUIMessageBox(TextManager.Get("DownloadSubLabel"), errorMsg,
- new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") })
- {
- UserData = "request" + serverSubmarine.Name
- };
- requestFileBox.Buttons[0].UserData = new FailedSubInfo(serverSubmarine.Name, serverSubmarine.MD5Hash.StringRepresentation);
- requestFileBox.Buttons[0].OnClicked += requestFileBox.Close;
- requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) =>
- {
- FailedSubInfo fileInfo = (FailedSubInfo)userdata;
-
- if (deliveryData == SubmarineDeliveryData.Owned)
- {
+ case SubmarineDeliveryData.Owned:
FailedOwnedSubs.Add(fileInfo);
- }
- else if (deliveryData == SubmarineDeliveryData.Campaign)
- {
+ break;
+ case SubmarineDeliveryData.Campaign:
FailedCampaignSubs.Add(fileInfo);
- }
+ break;
+ }
- GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo.Name, fileInfo.Hash);
- return true;
- };
- requestFileBox.Buttons[1].OnClicked += requestFileBox.Close;
+ GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo.Name, fileInfo.Hash);
return false;
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs
index 5ec47b586..bd2b9b4c9 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs
@@ -1905,27 +1905,27 @@ namespace Barotrauma
toolTip = TextManager.GetWithVariable("ServerListIncompatibleVersion", "[version]", serverInfo.GameVersion);
}
+ int maxIncompatibleToList = 10;
+ List incompatibleModNames = new List();
for (int i = 0; i < serverInfo.ContentPackageNames.Count; i++)
{
- bool listAsIncompatible = false;
- if (serverInfo.ContentPackageWorkshopIds[i] == 0)
- {
- listAsIncompatible = !ContentPackageManager.EnabledPackages.All.Any(contentPackage => contentPackage.Hash.StringRepresentation == serverInfo.ContentPackageHashes[i]);
- }
- else
- {
- listAsIncompatible = ContentPackageManager.EnabledPackages.All.Any(contentPackage => contentPackage.Hash.StringRepresentation != serverInfo.ContentPackageHashes[i] &&
- contentPackage.SteamWorkshopId == serverInfo.ContentPackageWorkshopIds[i]);
- }
+ bool listAsIncompatible = !ContentPackageManager.EnabledPackages.All.Any(contentPackage => contentPackage.Hash.StringRepresentation == serverInfo.ContentPackageHashes[i]);
if (listAsIncompatible)
{
- if (toolTip != "") toolTip += "\n";
- toolTip += TextManager.GetWithVariables("ServerListIncompatibleContentPackage",
- ("[contentpackage]", serverInfo.ContentPackageNames[i]),
- ("[hash]", Md5Hash.GetShortHash(serverInfo.ContentPackageHashes[i])));
+ incompatibleModNames.Add(TextManager.GetWithVariables("ModNameAndHashFormat",
+ ("[name]", serverInfo.ContentPackageNames[i]),
+ ("[hash]", Md5Hash.GetShortHash(serverInfo.ContentPackageHashes[i]))));
+
+ }
+ }
+ if (incompatibleModNames.Any())
+ {
+ toolTip += '\n' + TextManager.Get("ModDownloadHeader") + "\n" + string.Join(", ", incompatibleModNames.Take(maxIncompatibleToList));
+ if (incompatibleModNames.Count > maxIncompatibleToList)
+ {
+ toolTip += '\n' + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (incompatibleModNames.Count - maxIncompatibleToList).ToString());
}
}
-
serverContent.Children.ForEach(c => c.ToolTip = toolTip);
serverName.TextColor *= 0.5f;
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
index 7e6abbd01..05723864f 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs
@@ -237,7 +237,7 @@ namespace Barotrauma
public override Camera Cam => cam;
public static XDocument AutoSaveInfo;
- private static readonly string autoSavePath = Path.Combine(SubmarineInfo.SavePath, ".AutoSaves");
+ private static readonly string autoSavePath = Path.Combine("Submarines", ".AutoSaves");
private static readonly string autoSaveInfoPath = Path.Combine(autoSavePath, "autosaves.xml");
private static string GetSubDescription()
@@ -678,7 +678,7 @@ namespace Barotrauma
//-----------------------------------------------
- showEntitiesPanel = new GUIFrame(new RectTransform(new Vector2(0.1f, 0.5f), GUI.Canvas)
+ showEntitiesPanel = new GUIFrame(new RectTransform(new Vector2(0.15f, 0.5f), GUI.Canvas)
{
MinSize = new Point(190, 0)
})
@@ -771,22 +771,26 @@ namespace Barotrauma
}
foreach (string subcategory in availableSubcategories)
{
- var tb = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), subcategoryList.Content.RectTransform),
+ var tb = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), subcategoryList.Content.RectTransform),
TextManager.Get("subcategory." + subcategory).Fallback(subcategory), font: GUIStyle.SmallFont)
{
UserData = subcategory,
Selected = !IsSubcategoryHidden(subcategory),
OnSelected = (GUITickBox obj) => { hiddenSubCategories[(string)obj.UserData] = !obj.Selected; return true; },
};
- if (tb.TextBlock.TextSize.X > tb.TextBlock.Rect.Width * 1.25f)
+ tb.TextBlock.Wrap = true;
+ }
+
+ GUITextBlock.AutoScaleAndNormalize(subcategoryList.Content.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
+ foreach (GUIComponent child in subcategoryList.Content.Children)
+ {
+ if (child is GUITickBox tb && tb.TextBlock.TextSize.X > tb.TextBlock.Rect.Width * 1.25f)
{
tb.ToolTip = tb.Text;
tb.Text = ToolBox.LimitString(tb.Text.Value, tb.Font, (int)(tb.TextBlock.Rect.Width * 1.25f));
}
}
- GUITextBlock.AutoScaleAndNormalize(subcategoryList.Content.Children.Where(c => c is GUITickBox).Select(c => ((GUITickBox)c).TextBlock));
-
showEntitiesPanel.RectTransform.NonScaledSize =
new Point(
(int)(paddedShowEntitiesPanel.RectTransform.Children.Max(c => (int)((c.GUIComponent as GUITickBox)?.TextBlock.TextSize.X ?? 0)) / paddedShowEntitiesPanel.RectTransform.RelativeSize.X),
@@ -2933,7 +2937,7 @@ namespace Barotrauma
if (deleteButtonHolder.FindChild("delete") is GUIButton deleteBtn)
{
deleteBtn.Enabled = userData is SubmarineInfo subInfo
- && (GetContentPackageIntrinsicallyTiedToSub(subInfo) != null || Path.GetDirectoryName(subInfo.FilePath) == SubmarineInfo.SavePath);
+ && GetContentPackageIntrinsicallyTiedToSub(subInfo) != null;
}
return true;
}
@@ -3102,8 +3106,9 @@ namespace Barotrauma
var loadedSub = Submarine.Load(new SubmarineInfo(filePath), true);
// set the submarine file path to the "default" value
- loadedSub.Info.FilePath = Path.Combine(SubmarineInfo.SavePath, $"{TextManager.Get("UnspecifiedSubFileName")}.sub");
- loadedSub.Info.Name = TextManager.Get("UnspecifiedSubFileName").Value;
+ var unspecifiedFileName = TextManager.Get("UnspecifiedSubFileName");
+ loadedSub.Info.FilePath = Path.Combine(ContentPackage.LocalModsDir, unspecifiedFileName.Value, $"{unspecifiedFileName}.sub");
+ loadedSub.Info.Name = unspecifiedFileName.Value;
try
{
loadedSub.Info.Name = loadedSub.Info.SubmarineElement.GetAttributeString("name", loadedSub.Info.Name);
@@ -3199,8 +3204,7 @@ namespace Barotrauma
//check that it's a local content package and only allow deletion if it is.
//(deleting from the Submarines folder is also currently allowed, but this is temporary)
var subPackage = GetContentPackageIntrinsicallyTiedToSub(sub);
- bool isInOldSavePath = Path.GetDirectoryName(sub.FilePath) == SubmarineInfo.SavePath;
- if (!ContentPackageManager.LocalPackages.Regular.Contains(subPackage) && !isInOldSavePath) { return; }
+ if (!ContentPackageManager.LocalPackages.Regular.Contains(subPackage)) { return; }
var msgBox = new GUIMessageBox(
TextManager.Get("DeleteDialogLabel"),
@@ -3216,10 +3220,6 @@ namespace Barotrauma
ContentPackageManager.LocalPackages.Refresh();
ContentPackageManager.EnabledPackages.DisableRemovedMods();
}
- else if (isInOldSavePath && File.Exists(sub.FilePath))
- {
- File.Delete(sub.FilePath);
- }
sub.Dispose();
SubmarineInfo.RefreshSavedSubs();
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs
index c809ad75f..823439ace 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Settings/SettingsMenu.cs
@@ -13,7 +13,7 @@ using OpenAL;
namespace Barotrauma
{
- public class SettingsMenu
+ class SettingsMenu
{
public static SettingsMenu? Instance { get; private set; }
@@ -709,7 +709,9 @@ namespace Barotrauma
GUIFrame content = CreateNewContentFrame(Tab.Mods);
content.RectTransform.RelativeSize = Vector2.One;
- workshopMenu = new WorkshopMenu(content);
+ workshopMenu = Screen.Selected is MainMenuScreen
+ ? (WorkshopMenu)new MutableWorkshopMenu(content)
+ : (WorkshopMenu)new ImmutableWorkshopMenu(content);
}
private void CreateBottomButtons()
@@ -729,7 +731,7 @@ namespace Barotrauma
OnClicked = (btn, obj) =>
{
GameSettings.SetCurrentConfig(unsavedConfig);
- WorkshopMenu.Apply();
+ if (WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu) { mutableWorkshopMenu.Apply(); }
GameSettings.SaveCurrentConfig();
mainFrame.Flash(color: GUIStyle.Green);
return false;
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/BBCode.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/BBCode.cs
similarity index 96%
rename from Barotrauma/BarotraumaClient/ClientSource/Steam/BBCode.cs
rename to Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/BBCode.cs
index 93b6b3235..72a4861b5 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Steam/BBCode.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/BBCode.cs
@@ -8,9 +8,9 @@ using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma.Steam
{
- public partial class WorkshopMenu
+ abstract partial class WorkshopMenu
{
- private readonly struct BBWord
+ protected readonly struct BBWord
{
[Flags]
public enum TagType
@@ -42,10 +42,10 @@ namespace Barotrauma.Steam
}
}
- private static readonly Regex bbTagRegex = new Regex(@"\[(.+?)\]",
+ protected static readonly Regex bbTagRegex = new Regex(@"\[(.+?)\]",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
- private static GUICustomComponent CreateBBCodeElement(string bbCode, GUIListBox container)
+ protected static GUICustomComponent CreateBBCodeElement(string bbCode, GUIListBox container)
{
Point cachedContainerSize = Point.Zero;
List bbWords = new List();
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs
new file mode 100644
index 000000000..f94a64186
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Immutable/ImmutableWorkshopMenu.cs
@@ -0,0 +1,41 @@
+#nullable enable
+using Microsoft.Xna.Framework;
+
+namespace Barotrauma.Steam
+{
+ sealed class ImmutableWorkshopMenu : WorkshopMenu
+ {
+ public ImmutableWorkshopMenu(GUIFrame parent) : base(parent)
+ {
+ var mainLayout
+ = new GUILayoutGroup(new RectTransform((0.5f, 1.0f), parent.RectTransform, Anchor.Center), isHorizontal: false);
+
+ Label(mainLayout, TextManager.Get("enabledcore"), GUIStyle.SubHeadingFont);
+ var coreBox = new GUIButton(
+ NewItemRectT(mainLayout), style: "GUITextBoxNoIcon", text: ContentPackageManager.EnabledPackages.Core!.Name, textAlignment: Alignment.CenterLeft)
+ {
+ CanBeFocused = false,
+ CanBeSelected = false
+ };
+ 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))
+ {
+ OnSelected = (component, o) => false,
+ HoverCursor = CursorState.Default
+ };
+ foreach (var p in ContentPackageManager.EnabledPackages.Regular)
+ {
+ var regularBox = new GUITextBlock(
+ new RectTransform((1.0f, 0.07f), regularList.Content.RectTransform), text: p.Name)
+ {
+ CanBeFocused = false
+ };
+ }
+
+ Label(mainLayout, TextManager.Get("CannotChangeMods"), GUIStyle.Font);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/ItemList.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/ItemList.cs
similarity index 99%
rename from Barotrauma/BarotraumaClient/ClientSource/Steam/ItemList.cs
rename to Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/ItemList.cs
index 5ab0a9d8a..3ae7116ea 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Steam/ItemList.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/ItemList.cs
@@ -12,7 +12,7 @@ using ItemOrPackage = Barotrauma.Either itemOrPackage.TryGet(out ContentPackage package)
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs
similarity index 97%
rename from Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu.cs
rename to Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs
index 59b42ef78..a66e87f55 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/MutableWorkshopMenu.cs
@@ -10,7 +10,7 @@ using ItemOrPackage = Barotrauma.Either tabContents;
+ protected readonly GUILayoutGroup tabber;
+ protected readonly Dictionary tabContents;
- private readonly GUIFrame contentFrame;
+ protected readonly GUIFrame contentFrame;
private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
@@ -39,15 +39,17 @@ namespace Barotrauma.Steam
private readonly GUIListBox popularModsList;
private readonly GUIListBox selfModsList;
- public WorkshopMenu(GUIFrame parent)
+ public MutableWorkshopMenu(GUIFrame parent) : base(parent)
{
- var mainLayout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: false);
+ var mainLayout
+ = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: false);
- tabber = new GUILayoutGroup(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), isHorizontal: true) { Stretch = true };
+ tabber = new GUILayoutGroup(new RectTransform((1.0f, 0.05f), mainLayout.RectTransform), isHorizontal: true)
+ { Stretch = true };
tabContents = new Dictionary();
contentFrame = new GUIFrame(new RectTransform((1.0f, 0.95f), mainLayout.RectTransform), style: null);
-
+
CreateInstalledModsTab(
out enabledCoreDropdown,
out enabledRegularModsList,
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/PublishTab.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs
similarity index 99%
rename from Barotrauma/BarotraumaClient/ClientSource/Steam/PublishTab.cs
rename to Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs
index 720121b09..ec55b9e72 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Steam/PublishTab.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/Mutable/PublishTab.cs
@@ -15,7 +15,7 @@ using Path = Barotrauma.IO.Path;
namespace Barotrauma.Steam
{
- public partial class WorkshopMenu
+ sealed partial class MutableWorkshopMenu : WorkshopMenu
{
private class LocalThumbnail : IDisposable
{
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/UiUtil.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs
similarity index 79%
rename from Barotrauma/BarotraumaClient/ClientSource/Steam/UiUtil.cs
rename to Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs
index 9293df567..910fe6d1f 100644
--- a/Barotrauma/BarotraumaClient/ClientSource/Steam/UiUtil.cs
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/UiUtil.cs
@@ -7,22 +7,22 @@ using Microsoft.Xna.Framework;
namespace Barotrauma.Steam
{
- public partial class WorkshopMenu
+ abstract partial class WorkshopMenu
{
- private static RectTransform NewItemRectT(GUILayoutGroup parent, float heightScale = 1.0f)
+ protected static RectTransform NewItemRectT(GUILayoutGroup parent, float heightScale = 1.0f)
=> new RectTransform((1.0f, 0.06f * heightScale), parent.RectTransform, Anchor.CenterLeft);
- private static void Spacer(GUILayoutGroup parent, float height = 0.03f)
+ protected static void Spacer(GUILayoutGroup parent, float height = 0.03f)
{
new GUIFrame(new RectTransform((1.0f, height), parent.RectTransform, Anchor.CenterLeft), style: null);
}
- private static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font, float heightScale = 1.0f)
+ protected static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font, float heightScale = 1.0f)
{
return new GUITextBlock(NewItemRectT(parent, heightScale), str, font: font);
}
- private static GUITextBox ScrollableTextBox(GUILayoutGroup parent, float heightScale, string text)
+ protected static GUITextBox ScrollableTextBox(GUILayoutGroup parent, float heightScale, string text)
{
var containingListBox = new GUIListBox(NewItemRectT(parent, heightScale));
var textBox = new GUITextBox(
@@ -53,12 +53,12 @@ namespace Barotrauma.Steam
return textBox;
}
- private static GUIDropDown DropdownEnum(
+ protected static GUIDropDown DropdownEnum(
GUILayoutGroup parent, Func textFunc, T currentValue,
Action setter) where T : Enum
=> Dropdown(parent, textFunc, (T[])Enum.GetValues(typeof(T)), currentValue, setter);
- private static GUIDropDown Dropdown(
+ protected static GUIDropDown Dropdown(
GUILayoutGroup parent, Func textFunc, IReadOnlyList values, T currentValue,
Action setter, float heightScale = 1.0f)
{
@@ -67,7 +67,7 @@ namespace Barotrauma.Steam
return dropdown;
}
- private static void SwapDropdownValues(
+ protected static void SwapDropdownValues(
GUIDropDown dropdown, Func textFunc, IReadOnlyList values, T currentValue,
Action setter)
{
@@ -88,10 +88,10 @@ namespace Barotrauma.Steam
};
}
- private static int Round(float v) => (int)MathF.Round(v);
- private static string Percentage(float v) => $"{Round(v * 100)}";
+ protected static int Round(float v) => (int)MathF.Round(v);
+ protected static string Percentage(float v) => $"{Round(v * 100)}";
- private struct ActionCarrier
+ protected struct ActionCarrier
{
public readonly Identifier Id;
public readonly Action Action;
@@ -102,7 +102,7 @@ namespace Barotrauma.Steam
}
}
- private GUIComponent CreateActionCarrier(GUIComponent parent, Identifier id, Action action)
+ protected GUIComponent CreateActionCarrier(GUIComponent parent, Identifier id, Action action)
=> new GUIFrame(new RectTransform(Vector2.Zero, parent.RectTransform), style: null)
{ UserData = new ActionCarrier(id, action) };
}
diff --git a/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs
new file mode 100644
index 000000000..1a3e8f53c
--- /dev/null
+++ b/Barotrauma/BarotraumaClient/ClientSource/Steam/WorkshopMenu/WorkshopMenu.cs
@@ -0,0 +1,12 @@
+#nullable enable
+
+namespace Barotrauma.Steam
+{
+ abstract partial class WorkshopMenu
+ {
+ public WorkshopMenu(GUIFrame parent)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj
index 65d8b3408..92e9d579c 100644
--- a/Barotrauma/BarotraumaClient/LinuxClient.csproj
+++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj
index d6beedd53..9f9f372a2 100644
--- a/Barotrauma/BarotraumaClient/MacClient.csproj
+++ b/Barotrauma/BarotraumaClient/MacClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj
index 539cc7653..8ffe5ccb4 100644
--- a/Barotrauma/BarotraumaClient/WindowsClient.csproj
+++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
Barotrauma
diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj
index dbda5fa53..ab23d88c7 100644
--- a/Barotrauma/BarotraumaServer/LinuxServer.csproj
+++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj
index 883d5184f..3e74a9d79 100644
--- a/Barotrauma/BarotraumaServer/MacServer.csproj
+++ b/Barotrauma/BarotraumaServer/MacServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/CharacterCampaignData.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/CharacterCampaignData.cs
index 98015ddf0..671dedf34 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/CharacterCampaignData.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/CharacterCampaignData.cs
@@ -129,7 +129,7 @@ namespace Barotrauma
public void ApplyWalletData(Character character)
{
- character.Wallet = new Wallet(WalletData);
+ character.Wallet = new Wallet(Option.Some(character), WalletData);
}
public XElement Save()
diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs
index d8cc3b09b..d509c9ae9 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs
@@ -135,7 +135,7 @@ namespace Barotrauma
DebugConsole.NewMessage("Saved campaigns:", Color.White);
for (int i = 0; i < saveFiles.Length; i++)
{
- DebugConsole.NewMessage(" " + i + ". " + saveFiles[i], Color.White);
+ DebugConsole.NewMessage(" " + i + ". " + saveFiles[i].FilePath, Color.White);
}
DebugConsole.ShowQuestionPrompt("Select a save file to load (0 - " + (saveFiles.Length - 1) + "):", (string selectedSave) =>
{
@@ -148,7 +148,7 @@ namespace Barotrauma
}
else
{
- LoadCampaign(saveFiles[saveIndex]);
+ LoadCampaign(saveFiles[saveIndex].FilePath);
}
});
}
@@ -166,28 +166,13 @@ namespace Barotrauma
///
/// There is a client-side implementation of the method in
///
- public bool AllowedToEndRound(Client client)
- {
- //allow ending the round if the client has permissions, is the owner, the only client in the server,
- //or if no-one has permissions
- return
- client.HasPermission(ClientPermissions.ManageRound) ||
- client.HasPermission(ClientPermissions.ManageCampaign) ||
- GameMain.Server.ConnectedClients.Count == 1 ||
- IsOwner(client) ||
- GameMain.Server.ConnectedClients.None(c =>
- c.InGame && (IsOwner(c) || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
- }
-
- ///
- /// There is a client-side implementation of the method in
- ///
- public bool AllowedToManageCampaign(Client client, ClientPermissions permissions = ClientPermissions.ManageCampaign)
+ public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
{
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
//or if no-one has management permissions
return
client.HasPermission(permissions) ||
+ client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Server.ConnectedClients.Count == 1 ||
IsOwner(client) ||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
@@ -519,7 +504,7 @@ namespace Barotrauma
walletsToCheck.Clear();
walletsToCheck.Add(0, Bank);
- foreach (Character character in Mission.GetSalaryEligibleCrew())
+ foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Player))
{
walletsToCheck.Add(character.ID, character.Wallet);
}
@@ -715,107 +700,102 @@ namespace Barotrauma
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
- bool allowedToManageCampaign = AllowedToManageCampaign(sender);
- if (AllowedToManageCampaign(sender))
+ Location location = Map.CurrentLocation;
+ int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
+ int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
+ int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
+ Wallet personalWallet = GetWallet(sender);
+
+ if (purchasedHullRepairs != PurchasedHullRepairs)
{
- Location location = Map.CurrentLocation;
- int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
- int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
- int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
- Wallet personalWallet = GetWallet(sender);
-
- if (purchasedHullRepairs != PurchasedHullRepairs)
+ switch (purchasedHullRepairs)
{
- switch (purchasedHullRepairs)
- {
- case true when personalWallet.CanAfford(hullRepairCost):
- personalWallet.Deduct(hullRepairCost);
- PurchasedHullRepairs = true;
- GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
- break;
- case false:
- PurchasedHullRepairs = false;
- personalWallet.Refund(hullRepairCost);
- break;
- }
+ case true when personalWallet.CanAfford(hullRepairCost):
+ personalWallet.Deduct(hullRepairCost);
+ PurchasedHullRepairs = true;
+ GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
+ break;
+ case false:
+ PurchasedHullRepairs = false;
+ personalWallet.Refund(hullRepairCost);
+ break;
}
+ }
- if (purchasedItemRepairs != PurchasedItemRepairs)
+ if (purchasedItemRepairs != PurchasedItemRepairs)
+ {
+ switch (purchasedItemRepairs)
{
- switch (purchasedItemRepairs)
- {
- case true when personalWallet.CanAfford(itemRepairCost):
- personalWallet.Deduct(itemRepairCost);
- PurchasedItemRepairs = true;
- GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
- break;
- case false:
- PurchasedItemRepairs = false;
- personalWallet.Refund(itemRepairCost);
- break;
- }
+ case true when personalWallet.CanAfford(itemRepairCost):
+ personalWallet.Deduct(itemRepairCost);
+ PurchasedItemRepairs = true;
+ GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
+ break;
+ case false:
+ PurchasedItemRepairs = false;
+ personalWallet.Refund(itemRepairCost);
+ break;
}
+ }
- if (purchasedLostShuttles != PurchasedLostShuttles)
+ if (purchasedLostShuttles != PurchasedLostShuttles)
+ {
+ if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
{
- if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
- {
- GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
- }
- else if (purchasedLostShuttles && personalWallet.TryDeduct(shuttleRetrieveCost))
- {
- PurchasedLostShuttles = true;
- GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
- }
- else if (!purchasedItemRepairs)
- {
- PurchasedLostShuttles = false;
- personalWallet.Refund(shuttleRetrieveCost);
- }
+ GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
}
-
- if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
+ else if (purchasedLostShuttles && personalWallet.TryDeduct(shuttleRetrieveCost))
{
- Map.SetLocation(currentLocIndex);
+ PurchasedLostShuttles = true;
+ GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
}
+ else if (!purchasedItemRepairs)
+ {
+ PurchasedLostShuttles = false;
+ personalWallet.Refund(shuttleRetrieveCost);
+ }
+ }
+ if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
+ {
+ Map.SetLocation(currentLocIndex);
+ }
+
+ if (AllowedToManageCampaign(sender, ClientPermissions.ManageMap))
+ {
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); }
CheckTooManyMissions(Map.CurrentLocation, sender);
}
-
- bool allowedToUseStore = AllowedToManageCampaign(sender, ClientPermissions.CampaignStore);
- if (allowedToManageCampaign || allowedToUseStore || AllowedToManageCampaign(sender, ClientPermissions.BuyItems))
+
+ var prevBuyCrateItems = new Dictionary>(CargoManager.ItemsInBuyCrate);
+ foreach (var store in prevBuyCrateItems)
{
- var prevBuyCrateItems = new Dictionary>(CargoManager.ItemsInBuyCrate);
- foreach (var store in prevBuyCrateItems)
+ foreach (var item in store.Value)
{
- foreach (var item in store.Value)
- {
- CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, -item.Quantity, sender);
- }
- }
- foreach (var store in buyCrateItems)
- {
- foreach (var item in store.Value)
- {
- CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
- }
- }
- var prevPurchasedItems = new Dictionary>(CargoManager.PurchasedItems);
- foreach (var store in prevPurchasedItems)
- {
- CargoManager.SellBackPurchasedItems(store.Key, store.Value);
- }
- foreach (var store in purchasedItems)
- {
- CargoManager.PurchaseItems(store.Key, store.Value, false, sender);
+ CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, -item.Quantity, sender);
}
}
+ foreach (var store in buyCrateItems)
+ {
+ foreach (var item in store.Value)
+ {
+ CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
+ }
+ }
+ var prevPurchasedItems = new Dictionary>(CargoManager.PurchasedItems);
+ foreach (var store in prevPurchasedItems)
+ {
+ CargoManager.SellBackPurchasedItems(store.Key, store.Value);
+ }
+ foreach (var store in purchasedItems)
+ {
+ CargoManager.PurchaseItems(store.Key, store.Value, false, sender);
+ }
bool allowedToSellSubItems = AllowedToManageCampaign(sender, ClientPermissions.SellSubItems);
- if (allowedToManageCampaign || allowedToUseStore || allowedToSellSubItems)
+ if (allowedToSellSubItems)
{
var prevSubSellCrateItems = new Dictionary>(CargoManager.ItemsInSellFromSubCrate);
foreach (var store in prevSubSellCrateItems)
@@ -834,7 +814,7 @@ namespace Barotrauma
}
}
bool allowedToSellInventoryItems = AllowedToManageCampaign(sender, ClientPermissions.SellInventoryItems);
- if (allowedToManageCampaign || allowedToUseStore || (allowedToSellInventoryItems && allowedToSellSubItems))
+ if (allowedToSellInventoryItems && allowedToSellSubItems)
{
// for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all
// sold items that are removed so they should be discarded on the next message
@@ -867,37 +847,34 @@ namespace Barotrauma
bool predicate(SoldItem i) => allowedToSellInventoryItems != (i.Origin == SoldItem.SellOrigin.Character);
}
- if (allowedToManageCampaign)
+ foreach (var (prefab, category, _) in purchasedUpgrades)
{
- foreach (var (prefab, category, _) in purchasedUpgrades)
- {
- UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
+ UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
- // unstable logging
- int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
- int level = UpgradeManager.GetUpgradeLevel(prefab, category);
- GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
- }
- foreach (var purchasedItemSwap in purchasedItemSwaps)
+ // unstable logging
+ int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
+ int level = UpgradeManager.GetUpgradeLevel(prefab, category);
+ GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
+ }
+ foreach (var purchasedItemSwap in purchasedItemSwaps)
+ {
+ if (purchasedItemSwap.ItemToInstall == null)
{
- if (purchasedItemSwap.ItemToInstall == null)
- {
- UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
- }
- else
- {
- UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, client: sender);
- }
+ UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
}
- foreach (Item item in Item.ItemList)
+ else
{
- if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
- {
- UpgradeManager.CancelItemSwap(item);
- item.PendingItemSwap = null;
- }
+ UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, client: sender);
}
}
+ foreach (Item item in Item.ItemList)
+ {
+ if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
+ {
+ UpgradeManager.CancelItemSwap(item);
+ item.PendingItemSwap = null;
+ }
+ }
}
public void ServerReadMoney(IReadMessage msg, Client sender)
@@ -907,19 +884,26 @@ namespace Barotrauma
switch (transfer.Sender)
{
case Some { Value: var id }:
-
- if (id != sender.CharacterID && !AllowedToManageCampaign(sender)) { return; }
+ if (id != sender.CharacterID && !AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; }
Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; }
TransferMoney(wallet);
break;
-
case None _:
- if (!AllowedToManageCampaign(sender)) { return; }
-
- TransferMoney(Bank);
+ if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney))
+ {
+ if (transfer.Receiver is Some { Value: var receiverId } && receiverId == sender.CharacterID)
+ {
+ GameMain.Server?.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
+ }
+ return;
+ }
+ else
+ {
+ TransferMoney(Bank);
+ }
break;
}
@@ -952,10 +936,10 @@ namespace Barotrauma
{
NetWalletSetSalaryUpdate update = INetSerializableStruct.Read(msg);
- if (!AllowedToManageCampaign(sender)) { return; }
+ if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; }
Character targetCharacter = Character.CharacterList.FirstOrDefault(c => c.ID == update.Target);
- targetCharacter?.Wallet.SetRewardDistrubiton(update.NewRewardDistribution);
+ targetCharacter?.Wallet.SetRewardDistribution(update.NewRewardDistribution);
}
public void ServerReadCrew(IReadMessage msg, Client sender)
@@ -994,7 +978,7 @@ namespace Barotrauma
List hiredCharacters = new List();
CharacterInfo firedCharacter = null;
- if (location != null && AllowedToManageCampaign(sender))
+ if (location != null && AllowedToManageCampaign(sender, ClientPermissions.ManageHires))
{
if (fireCharacter)
{
diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs
index 6f19e2077..e9c23978c 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs
@@ -18,7 +18,7 @@ namespace Barotrauma
private struct RateLimitInfo
{
public int Requests;
- public const int MaxRequests = 5;
+ public const int MaxRequests = 10;
public DateTimeOffset Expiry;
}
diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs
index 4373de52d..0e2b53f6e 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs
@@ -130,6 +130,8 @@ namespace Barotrauma.Networking
KarmaManager.SelectPreset(serverSettings.KarmaPreset);
serverSettings.SetPassword(password);
+ Voting = new Voting();
+
ownerKey = ownKey;
ownerSteamId = steamId;
@@ -383,23 +385,7 @@ namespace Barotrauma.Networking
TraitorManager?.Update(deltaTime);
- if (serverSettings.Voting.VoteRunning)
- {
- Voting.SubVote.Timer += deltaTime;
-
- if (Voting.SubVote.Timer >= serverSettings.SubmarineVoteTimeout)
- {
- // Do not take unanswered into account for total
- if (SubmarineVoteYesCount / (float)(SubmarineVoteYesCount + SubmarineVoteNoCount) >= serverSettings.SubmarineVoteRequiredRatio)
- {
- SwitchSubmarine();
- }
- else
- {
- serverSettings.Voting.StopSubmarineVote(false);
- }
- }
- }
+ Voting.Update(deltaTime);
bool isCrewDead =
connectedClients.All(c => c.Character == null || c.Character.IsDead || c.Character.IsIncapacitated);
@@ -1073,7 +1059,7 @@ namespace Barotrauma.Networking
ChatMessage.ServerRead(inc, c);
break;
case ClientNetObject.VOTE:
- serverSettings.Voting.ServerRead(inc, c);
+ Voting.ServerRead(inc, c);
break;
default:
return;
@@ -1220,7 +1206,7 @@ namespace Barotrauma.Networking
entityEventManager.Read(inc, c);
break;
case ClientNetObject.VOTE:
- serverSettings.Voting.ServerRead(inc, c);
+ Voting.ServerRead(inc, c);
break;
case ClientNetObject.SPECTATING_POS:
c.SpectatePos = new Vector2(inc.ReadSingle(), inc.ReadSingle());
@@ -1309,17 +1295,11 @@ namespace Barotrauma.Networking
var mpCampaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
if (command == ClientPermissions.ManageRound && mpCampaign != null)
{
- if (!mpCampaign.AllowedToEndRound(sender))
- {
- return;
- }
+ //do nothing, ending campaign rounds is checked in more detail below
}
else if (command == ClientPermissions.ManageCampaign && mpCampaign != null)
{
- if (!mpCampaign.AllowedToManageCampaign(sender))
- {
- return;
- }
+ //do nothing, campaign permissions are checked in more detail in MultiplayerCampaign.ServerRead
}
else if (!sender.HasPermission(command))
{
@@ -1381,21 +1361,26 @@ namespace Barotrauma.Networking
bool end = inc.ReadBoolean();
if (end)
{
- bool save = inc.ReadBoolean();
- if (gameStarted)
+ if (mpCampaign == null ||
+ mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageRound) ||
+ mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign))
{
- Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
- if (mpCampaign != null && Level.IsLoadedOutpost && save)
+ bool save = inc.ReadBoolean();
+ if (gameStarted)
{
- mpCampaign.SavePlayers();
- GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
- SaveUtil.SaveGame(GameMain.GameSession.SavePath);
+ Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
+ if (mpCampaign != null && Level.IsLoadedOutpost && save)
+ {
+ mpCampaign.SavePlayers();
+ GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
+ SaveUtil.SaveGame(GameMain.GameSession.SavePath);
+ }
+ else
+ {
+ save = false;
+ }
+ EndGame(wasSaved: save);
}
- else
- {
- save = false;
- }
- EndGame(wasSaved: save);
}
}
else
@@ -1403,14 +1388,17 @@ namespace Barotrauma.Networking
bool continueCampaign = inc.ReadBoolean();
if (mpCampaign != null && mpCampaign.GameOver || continueCampaign)
{
- MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
+ if (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap))
+ {
+ MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
+ }
}
else if (!gameStarted && !initiatedStartGame)
{
Log("Client \"" + ClientLogName(sender) + "\" started the round.", ServerLog.MessageType.ServerMessage);
StartGame();
}
- else if (mpCampaign != null)
+ else if (mpCampaign != null && (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)))
{
var availableTransition = mpCampaign.GetAvailableTransition(out _, out _);
//don't force location if we've teleported
@@ -1473,34 +1461,20 @@ namespace Barotrauma.Networking
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier == "multiplayercampaign")
{
- string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false).ToArray();
- for (int i = 0; i < saveFiles.Length; i++)
- {
- XDocument doc = SaveUtil.LoadGameSessionDoc(saveFiles[i]);
- if (doc?.Root != null)
- {
- saveFiles[i] =
- string.Join(";",
- saveFiles[i].Replace(';', ' '),
- doc.Root.GetAttributeStringUnrestricted("submarine", ""),
- doc.Root.GetAttributeStringUnrestricted("savetime", ""),
- doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", ""));
- }
- }
-
+ const int MaxSaves = 255;
+ var saveInfos = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false);
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
- msg.Write((UInt16)saveFiles.Count());
- foreach (string saveFile in saveFiles)
+ msg.Write((byte)Math.Min(saveInfos.Count, MaxSaves));
+ for (int i = 0; i < saveInfos.Count && i < MaxSaves; i++)
{
- msg.Write(saveFile);
+ msg.Write(saveInfos[i]);
}
-
serverPeer.Send(msg, sender.Connection, DeliveryMethod.Reliable);
}
break;
case ClientPermissions.ManageCampaign:
- (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender);
+ mpCampaign?.ServerRead(inc, sender);
break;
case ClientPermissions.ConsoleCommands:
{
@@ -1900,8 +1874,8 @@ namespace Barotrauma.Networking
outmsg.Write(selectedShuttle.Name);
outmsg.Write(selectedShuttle.MD5Hash.ToString());
- outmsg.Write(serverSettings.Voting.AllowSubVoting);
- outmsg.Write(serverSettings.Voting.AllowModeVoting);
+ outmsg.Write(serverSettings.AllowSubVoting);
+ outmsg.Write(serverSettings.AllowModeVoting);
outmsg.Write(serverSettings.VoiceChatEnabled);
@@ -2036,9 +2010,9 @@ namespace Barotrauma.Networking
SubmarineInfo selectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle;
SubmarineInfo selectedSub;
- if (serverSettings.Voting.AllowSubVoting)
+ if (serverSettings.AllowSubVoting)
{
- selectedSub = serverSettings.Voting.HighestVoted(VoteType.Sub, connectedClients);
+ selectedSub = Voting.HighestVoted(VoteType.Sub, connectedClients);
if (selectedSub == null) { selectedSub = GameMain.NetLobbyScreen.SelectedSub; }
}
else
@@ -2051,7 +2025,7 @@ namespace Barotrauma.Networking
return false;
}
- GameModePreset selectedMode = serverSettings.Voting.HighestVoted(VoteType.Mode, connectedClients);
+ GameModePreset selectedMode = Voting.HighestVoted(VoteType.Mode, connectedClients);
if (selectedMode == null) { selectedMode = GameMain.NetLobbyScreen.SelectedMode; }
if (selectedMode == null)
@@ -2455,11 +2429,8 @@ namespace Barotrauma.Networking
yield return CoroutineStatus.Running;
- if (GameMain.Server?.ServerSettings?.Voting != null)
- {
- GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
- }
-
+ Voting?.ResetVotes(GameMain.Server.ConnectedClients);
+
GameMain.GameScreen.Select();
Log("Round started.", ServerLog.MessageType.ServerMessage);
@@ -3233,23 +3204,24 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, transfer.Connection, DeliveryMethod.ReliableOrdered);
}
- public void UpdateVoteStatus()
+ public void UpdateVoteStatus(bool checkActiveVote = true)
{
- if (connectedClients.Count == 0) return;
+ if (connectedClients.Count == 0) { return; }
- if (serverSettings.Voting.VoteRunning)
+ if (checkActiveVote && Voting.ActiveVote != null)
{
+ int yes = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote(Voting.ActiveVote.VoteType) == 2);
+ int no = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote(Voting.ActiveVote.VoteType) == 1);
+ int max = GameMain.Server.ConnectedClients.Count(c => c.InGame);
// Required ratio cannot be met
- if (SubmarineVoteNoCount / (float)SubmarineVoteMax > 1f - serverSettings.SubmarineVoteRequiredRatio)
+ if (no / (float)max > 1f - serverSettings.VoteRequiredRatio)
{
- serverSettings.Voting.StopSubmarineVote(false);
- return; // Update will be re-sent via StopSubmarineVote
+ Voting.ActiveVote.Finish(Voting, passed: false);
}
- else if (SubmarineVoteYesCount / (float)SubmarineVoteMax >= serverSettings.SubmarineVoteRequiredRatio)
+ else if (yes / max >= serverSettings.VoteRequiredRatio)
{
- SwitchSubmarine();
- return; // Update will be re-sent via StopSubmarineVote
- }
+ Voting.ActiveVote.Finish(Voting, passed: true);
+ }
}
Client.UpdateKickVotes(connectedClients);
@@ -3279,10 +3251,12 @@ namespace Barotrauma.Networking
SendVoteStatus(connectedClients);
- if (serverSettings.Voting.AllowEndVoting && EndVoteMax > 0 &&
- ((float)EndVoteCount / (float)EndVoteMax) >= serverSettings.EndVoteRequiredRatio)
+ int endVoteCount = ConnectedClients.Count(c => c.HasSpawned && c.GetVote(VoteType.EndRound));
+ int endVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
+ if (serverSettings.AllowEndVoting && endVoteMax > 0 &&
+ ((float)endVoteCount / (float)endVoteMax) >= serverSettings.EndVoteRequiredRatio)
{
- Log("Ending round by votes (" + EndVoteCount + "/" + (EndVoteMax - EndVoteCount) + ")", ServerLog.MessageType.ServerMessage);
+ Log("Ending round by votes (" + endVoteCount + "/" + (endVoteMax - endVoteCount) + ")", ServerLog.MessageType.ServerMessage);
EndGame(wasSaved: false);
}
}
@@ -3294,7 +3268,7 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
msg.Write((byte)ServerNetObject.VOTE);
- serverSettings.Voting.ServerWrite(msg);
+ Voting.ServerWrite(msg);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
foreach (var c in recipients)
@@ -3303,11 +3277,13 @@ namespace Barotrauma.Networking
}
}
- private void SwitchSubmarine()
+ public void SwitchSubmarine()
{
- SubmarineInfo targetSubmarine = Voting.SubVote.Sub;
- VoteType voteType = Voting.SubVote.VoteType;
- Client starter = Voting.SubVote.VoteStarter;
+ if (!(Voting.ActiveVote is Voting.SubmarineVote subVote)) { return; }
+
+ SubmarineInfo targetSubmarine = subVote.Sub;
+ VoteType voteType = Voting.ActiveVote.VoteType;
+ Client starter = Voting.ActiveVote.VoteStarter;
int deliveryFee = 0;
switch (voteType)
@@ -3318,7 +3294,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.PurchaseSubmarine(targetSubmarine, starter);
break;
case VoteType.SwitchSub:
- deliveryFee = Voting.SubVote.DeliveryFee;
+ deliveryFee = subVote.DeliveryFee;
break;
default:
return;
@@ -3326,10 +3302,10 @@ namespace Barotrauma.Networking
if (voteType != VoteType.PurchaseSub)
{
- SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee, starter);
+ GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee, starter);
}
- serverSettings.Voting.StopSubmarineVote(true);
+ Voting.StopSubmarineVote(true);
}
public void UpdateClientPermissions(Client client)
diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs
index 5580a12fe..6bf8c9216 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs
@@ -312,8 +312,8 @@ namespace Barotrauma.Networking
AutoRestart = doc.Root.GetAttributeBool("autorestart", false);
- Voting.AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
- Voting.AllowModeVoting = ModeSelectionMode == SelectionMode.Vote;
+ AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
+ AllowModeVoting = ModeSelectionMode == SelectionMode.Vote;
selectedLevelDifficulty = doc.Root.GetAttributeFloat("LevelDifficulty", 20.0f);
GameMain.NetLobbyScreen.SetLevelDifficulty(selectedLevelDifficulty);
diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs
index f6d9cf671..985bcdf3c 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Voting.cs
@@ -7,59 +7,161 @@ namespace Barotrauma
{
partial class Voting
{
- public bool AllowSubVoting
+ public interface IVote
{
- get { return allowSubVoting; }
- set { allowSubVoting = value; }
- }
- public bool AllowModeVoting
- {
- get { return allowModeVoting; }
- set { allowModeVoting = value; }
- }
+ public Client VoteStarter { get; }
+ public VoteType VoteType { get; }
+ public float Timer { get; set; }
- public struct SubmarineVote
+ public VoteState State { get; set; }
+
+ public void Finish(Voting voting, bool passed);
+ }
+
+ public class SubmarineVote : IVote
{
- public Client VoteStarter;
+ public Client VoteStarter { get; }
+ public VoteType VoteType { get; }
+ public float Timer { get; set; }
+
+ public VoteState State { get; set; }
+
public SubmarineInfo Sub;
- public VoteType VoteType;
- public float Timer;
public int DeliveryFee;
- public VoteState State;
+
+ public SubmarineVote(Client starter, SubmarineInfo subInfo, int deliveryFee, VoteType voteType)
+ {
+ Sub = subInfo;
+ DeliveryFee = deliveryFee;
+ VoteType = voteType;
+ State = VoteState.Started;
+ VoteStarter = starter;
+ }
+
+ public void Finish(Voting voting, bool passed)
+ {
+ if (passed)
+ {
+ GameMain.Server?.SwitchSubmarine();
+ }
+ voting.StopSubmarineVote(passed);
+ }
}
- public static SubmarineVote SubVote;
+ public static IVote ActiveVote;
+
+ public class TransferVote : IVote
+ {
+ public Client VoteStarter { get; }
+ public VoteType VoteType { get; }
+ public float Timer { get; set; }
+
+ public VoteState State { get; set; }
+
+ //null = bank
+ public readonly Client From, To;
+ public readonly int TransferAmount;
+
+ public TransferVote(Client starter, Client from, int transferAmount, Client to)
+ {
+ VoteStarter = starter;
+ From = from;
+ To = to;
+ TransferAmount = transferAmount;
+ State = VoteState.Started;
+ VoteType = VoteType.TransferMoney;
+ }
+
+ public void Finish(Voting voting, bool passed)
+ {
+ if (passed)
+ {
+ Wallet fromWallet = From == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : From.Character?.Wallet;
+ if (fromWallet.TryDeduct(TransferAmount))
+ {
+ Wallet toWallet = To == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : To.Character?.Wallet;
+ toWallet.Give(TransferAmount);
+ }
+ }
+ voting.StopMoneyTransferVote(passed);
+ }
+ }
+
+ private static readonly Queue pendingVotes = new Queue();
private void StartSubmarineVote(SubmarineInfo subInfo, VoteType voteType, Client sender)
{
- SubVote.Sub = subInfo;
- SubVote.DeliveryFee = voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0;
- SubVote.VoteType = voteType;
- SubVote.State = VoteState.Started;
- SubVote.VoteStarter = sender;
- VoteRunning = true;
+ var subVote = new SubmarineVote(
+ sender,
+ subInfo,
+ voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0,
+ voteType);
+ StartOrEnqueueVote(subVote);
sender.SetVote(voteType, 2);
+ GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
}
public void StopSubmarineVote(bool passed)
{
- VoteRunning = false;
- SubVote.State = passed ? VoteState.Passed : VoteState.Failed;
+ if (!(ActiveVote is SubmarineVote)) { return; }
+ StopActiveVote(passed);
+ }
- GameMain.Server.UpdateVoteStatus();
+ public void StopMoneyTransferVote(bool passed)
+ {
+ if (!(ActiveVote is TransferVote)) { return; }
+ StopActiveVote(passed);
+ }
+
+ public void StopActiveVote(bool passed)
+ {
+ ActiveVote.State = passed ? VoteState.Passed : VoteState.Failed;
+ GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
- GameMain.NetworkMember.SubmarineVoteYesCount = GameMain.NetworkMember.SubmarineVoteNoCount = GameMain.NetworkMember.SubmarineVoteMax = 0;
for (int i = 0; i < GameMain.NetworkMember.ConnectedClients.Count; i++)
{
- GameMain.NetworkMember.ConnectedClients[i].SetVote(SubVote.VoteType, 0);
+ GameMain.NetworkMember.ConnectedClients[i].SetVote(ActiveVote.VoteType, 0);
}
- SubVote.Sub = null;
- SubVote.DeliveryFee = 0;
- SubVote.VoteType = VoteType.Unknown;
- SubVote.Timer = 0.0f;
- SubVote.State = VoteState.None;
- SubVote.VoteStarter = null;
+ ActiveVote = null;
+ if (pendingVotes.Any())
+ {
+ ActiveVote = pendingVotes.Dequeue();
+ }
+ }
+
+ public void StartTransferVote(Client starter, Client from, int transferAmount, Client to)
+ {
+ StartOrEnqueueVote(new TransferVote(starter, from, transferAmount, to));
+ starter.SetVote(VoteType.TransferMoney, 2);
+ GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
+ }
+
+ private void StartOrEnqueueVote(IVote vote)
+ {
+ if (ActiveVote == null)
+ {
+ ActiveVote = vote;
+ }
+ else
+ {
+ pendingVotes.Enqueue(vote);
+ }
+ }
+
+ public void Update(float deltaTime)
+ {
+ if (ActiveVote == null) { return; }
+
+ ActiveVote.Timer += deltaTime;
+
+ if (ActiveVote.Timer >= GameMain.NetworkMember.ServerSettings.VoteTimeout)
+ {
+ // Do not take unanswered into account for total
+ int yes = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote(ActiveVote.VoteType) == 2);
+ int no = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote(ActiveVote.VoteType) == 1);
+ ActiveVote.Finish(this, passed: yes / (float)(yes + no) >= GameMain.NetworkMember.ServerSettings.VoteRequiredRatio);
+ }
}
public void ServerRead(IReadMessage inc, Client sender)
@@ -97,10 +199,6 @@ namespace Barotrauma
case VoteType.EndRound:
if (!sender.HasSpawned) { return; }
sender.SetVote(voteType, inc.ReadBoolean());
-
- GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote(VoteType.EndRound));
- GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
-
break;
case VoteType.Kick:
byte kickedClientID = inc.ReadByte();
@@ -126,24 +224,34 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub:
case VoteType.PurchaseSub:
case VoteType.SwitchSub:
+ case VoteType.TransferMoney:
bool startVote = inc.ReadBoolean();
if (startVote)
{
- string subName = inc.ReadString();
- SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
- if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
+ if (voteType == VoteType.TransferMoney)
{
- StartSubmarineVote(subInfo, voteType, sender);
+ int amount = inc.ReadInt32();
+ int fromClientId = inc.ReadByte();
+ int toClientId = inc.ReadByte();
+ pendingVotes.Enqueue(new TransferVote(sender,
+ GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
+ amount,
+ GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
+ }
+ else
+ {
+ string subName = inc.ReadString();
+ SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
+ if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
+ {
+ StartSubmarineVote(subInfo, voteType, sender);
+ }
}
}
else
{
sender.SetVote(voteType, (int)inc.ReadByte());
}
-
- GameMain.Server.SubmarineVoteYesCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote(SubVote.VoteType) == 2);
- GameMain.Server.SubmarineVoteNoCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote(SubVote.VoteType) == 1);
- GameMain.Server.SubmarineVoteMax = GameMain.Server.ConnectedClients.Count(c => c.InGame);
break;
}
@@ -154,10 +262,10 @@ namespace Barotrauma
public void ServerWrite(IWriteMessage msg)
{
- if (GameMain.Server == null) return;
+ if (GameMain.Server == null) { return; }
- msg.Write(allowSubVoting);
- if (allowSubVoting)
+ msg.Write(GameMain.Server.ServerSettings.AllowSubVoting);
+ if (GameMain.Server.ServerSettings.AllowSubVoting)
{
IReadOnlyDictionary voteList = GetVoteCounts(VoteType.Sub, GameMain.Server.ConnectedClients);
msg.Write((byte)voteList.Count);
@@ -167,8 +275,8 @@ namespace Barotrauma
msg.Write(vote.Key.Name);
}
}
- msg.Write(AllowModeVoting);
- if (allowModeVoting)
+ msg.Write(GameMain.Server.ServerSettings.AllowModeVoting);
+ if (GameMain.Server.ServerSettings.AllowModeVoting)
{
IReadOnlyDictionary voteList = GetVoteCounts(VoteType.Mode, GameMain.Server.ConnectedClients);
msg.Write((byte)voteList.Count);
@@ -178,60 +286,78 @@ namespace Barotrauma
msg.Write(vote.Key.Identifier);
}
}
- msg.Write(AllowEndVoting);
- if (AllowEndVoting)
+ msg.Write(GameMain.Server.ServerSettings.AllowEndVoting);
+ if (GameMain.Server.ServerSettings.AllowEndVoting)
{
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote(VoteType.EndRound)));
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
}
- msg.Write(AllowVoteKick);
+ msg.Write(GameMain.Server.ServerSettings.AllowVoteKick);
- msg.Write((byte)SubVote.State);
- if (SubVote.State != VoteState.None)
+ msg.Write((byte)(ActiveVote?.State ?? VoteState.None));
+ if (ActiveVote != null)
{
- msg.Write((byte)SubVote.VoteType);
-
- if (SubVote.VoteType != VoteType.Unknown)
- {
- var yesClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote(SubVote.VoteType) == 2);
+ msg.Write((byte)ActiveVote.VoteType);
+ if (ActiveVote.State != VoteState.None && ActiveVote.VoteType != VoteType.Unknown)
+ {
+ var yesClients = GameMain.Server.ConnectedClients.FindAll(c => c.InGame && c.GetVote(ActiveVote.VoteType) == 2);
msg.Write((byte)yesClients.Count);
foreach (Client c in yesClients)
{
msg.Write(c.ID);
}
- var noClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote(SubVote.VoteType) == 1);
+ var noClients = GameMain.Server.ConnectedClients.FindAll(c => c.InGame && c.GetVote(ActiveVote.VoteType) == 1);
msg.Write((byte)noClients.Count);
foreach (Client c in noClients)
{
msg.Write(c.ID);
}
- msg.Write((byte)GameMain.Server.SubmarineVoteMax);
+ msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.InGame));
- switch (SubVote.State)
+ switch (ActiveVote.State)
{
case VoteState.Started:
- msg.Write(SubVote.Sub.Name);
- msg.Write(SubVote.VoteStarter.ID);
- msg.Write((byte)GameMain.Server.ServerSettings.SubmarineVoteTimeout);
+ msg.Write(ActiveVote.VoteStarter.ID);
+ msg.Write((byte)GameMain.Server.ServerSettings.VoteTimeout);
+
+ switch (ActiveVote.VoteType)
+ {
+ case VoteType.PurchaseSub:
+ case VoteType.PurchaseAndSwitchSub:
+ case VoteType.SwitchSub:
+ msg.Write((ActiveVote as SubmarineVote).Sub.Name);
+ break;
+ case VoteType.TransferMoney:
+ var transferVote = (ActiveVote as TransferVote);
+ msg.Write(transferVote.From?.ID ?? 0);
+ msg.Write(transferVote.To?.ID ?? 0);
+ msg.Write(transferVote.TransferAmount);
+ break;
+ }
+
break;
case VoteState.Running:
// Nothing specific
break;
case VoteState.Passed:
case VoteState.Failed:
- msg.Write(SubVote.State == VoteState.Passed);
- msg.Write(SubVote.Sub.Name);
- if (SubVote.State == VoteState.Passed)
+ msg.Write(ActiveVote.State == VoteState.Passed);
+ switch (ActiveVote.VoteType)
{
- msg.Write((short)SubVote.DeliveryFee);
+ case VoteType.PurchaseSub:
+ case VoteType.PurchaseAndSwitchSub:
+ case VoteType.SwitchSub:
+ msg.Write((ActiveVote as SubmarineVote).Sub.Name);
+ msg.Write((short)(ActiveVote as SubmarineVote).DeliveryFee);
+ break;
}
break;
- }
+ }
}
- }
+ }
var readyClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote(VoteType.StartRound));
msg.Write((byte)readyClients.Count);
diff --git a/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs
index aaccf295c..e746fac64 100644
--- a/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs
+++ b/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs
@@ -192,7 +192,7 @@ namespace Barotrauma
public override void Select()
{
base.Select();
- GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
+ GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients);
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
{
GameMain.GameSession = null;
diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj
index 96429c964..86864cc3c 100644
--- a/Barotrauma/BarotraumaServer/WindowsServer.csproj
+++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj
@@ -6,7 +6,7 @@
Barotrauma
FakeFish, Undertow Games
Barotrauma Dedicated Server
- 0.17.4.0
+ 0.17.5.0
Copyright © FakeFish 2018-2022
AnyCPU;x64
DedicatedServer
diff --git a/Barotrauma/BarotraumaShared/Data/permissionpresets.xml b/Barotrauma/BarotraumaShared/Data/permissionpresets.xml
index 0bad02e51..efff061b6 100644
--- a/Barotrauma/BarotraumaShared/Data/permissionpresets.xml
+++ b/Barotrauma/BarotraumaShared/Data/permissionpresets.xml
@@ -8,7 +8,7 @@
+ permissions="ManageRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog,ManageSettings,ManageMoney">
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs
index c63af2b86..4d307d4c7 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs
@@ -130,7 +130,7 @@ namespace Barotrauma
}
}
- private Wallet wallet = new Wallet();
+ private Wallet wallet;
public Wallet Wallet
{
@@ -1055,8 +1055,13 @@ namespace Barotrauma
return newCharacter;
}
+ private Character(Submarine submarine, ushort id): base(submarine, id)
+ {
+ wallet = new Wallet(Option.Some(this));
+ }
+
protected Character(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, RagdollParams ragdollParams = null)
- : base(null, id)
+ : this(null, id)
{
this.Seed = seed;
this.Prefab = prefab;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs
index 0758b7042..70f3ebdaf 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs
@@ -172,11 +172,21 @@ namespace Barotrauma
SetCore(ContentPackageManager.WorkshopPackages.Core.FirstOrDefault(p => p.SteamWorkshopId == Core.SteamWorkshopId) ??
ContentPackageManager.CorePackages.First());
}
- SetRegular(Regular
- .Select(p => ContentPackageManager.RegularPackages.Contains(p)
- ? p
- : ContentPackageManager.WorkshopPackages.Regular.FirstOrDefault(p2 => p2.SteamWorkshopId == p.SteamWorkshopId))
- .ToArray());
+
+ List newRegular = new List();
+ foreach (var p in Regular)
+ {
+ if (ContentPackageManager.RegularPackages.Contains(p))
+ {
+ newRegular.Add(p);
+ }
+ else if (ContentPackageManager.WorkshopPackages.Regular.FirstOrDefault(p2
+ => p2.SteamWorkshopId == p.SteamWorkshopId) is { } newP)
+ {
+ newRegular.Add(newP);
+ }
+ }
+ SetRegular(newRegular);
}
public static void BackUp()
diff --git a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs
index 5e378bcb0..5711e7769 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentXElement.cs
@@ -115,6 +115,24 @@ namespace Barotrauma
}
public void Remove() => Element.Remove();
+
+ public override bool Equals(object? obj)
+ {
+ return obj is ContentXElement element && this == element;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(ContentPackage, Element);
+ }
+
+ public static bool operator ==(in ContentXElement? a, in ContentXElement? b)
+ {
+ return a?.ContentPackage == b?.ContentPackage && a?.Element == b?.Element;
+ }
+
+ public static bool operator !=(in ContentXElement? a, in ContentXElement? b) =>
+ !(a == b);
}
public static class ContentXElementExtensions
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Enums.cs b/Barotrauma/BarotraumaShared/SharedSource/Enums.cs
index 6bba12392..94123f36e 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Enums.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Enums.cs
@@ -1,4 +1,6 @@
-namespace Barotrauma
+using System;
+
+namespace Barotrauma
{
public enum TransitionMode
{
@@ -146,4 +148,11 @@
AlwaysStayConscious,
}
+ [Flags]
+ public enum CharacterType
+ {
+ Bot = 0b01,
+ Player = 0b10,
+ Both = Bot | Player
+ }
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs
index 413e835e1..dc062b81e 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs
@@ -350,7 +350,7 @@ namespace Barotrauma
float difficultyMultiplier = 1 + level.Difficulty / 100f;
baseExperienceGain *= difficultyMultiplier;
- IEnumerable crewCharacters = GameSession.GetSessionCrewCharacters();
+ IEnumerable crewCharacters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
// use multipliers here so that we can easily add them together without introducing multiplicative XP stacking
var experienceGainMultiplier = new AbilityExperienceGainMultiplier(1f);
@@ -380,7 +380,7 @@ namespace Barotrauma
GameAnalyticsManager.AddMoneyGainedEvent(totalReward, GameAnalyticsManager.MoneySource.MissionReward, Prefab.Identifier.Value);
#if SERVER
- totalReward = DistributeRewardsToCrew(GetSalaryEligibleCrew(), totalReward);
+ totalReward = DistributeRewardsToCrew(GameSession.GetSessionCrewCharacters(CharacterType.Player), totalReward);
#endif
if (totalReward > 0)
{
@@ -436,18 +436,6 @@ namespace Barotrauma
}
#endif
- public static IEnumerable GetSalaryEligibleCrew()
- {
- if (!(GameMain.GameSession.CrewManager is { } crewManager)) { return Array.Empty(); }
-
- IEnumerable characters = crewManager.GetCharacters();
-#if SERVER
- return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead).Concat(characters);
-#elif CLIENT
- return characters;
-#endif
- }
-
public static int GetRewardDistibutionSum(IEnumerable crew, int rewardDistribution = 0) => crew.Sum(c => c.Wallet.RewardDistribution) + rewardDistribution;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs
index 192830b91..0ac70c5c4 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs
@@ -95,7 +95,7 @@ namespace Barotrauma
public readonly bool IsSideObjective;
- public readonly bool RequireWreck;
+ public readonly bool RequireWreck, RequireRuin;
///
/// The mission can only be received when travelling from a location of the first type to a location of the second type
@@ -152,6 +152,7 @@ namespace Barotrauma
AllowRetry = element.GetAttributeBool("allowretry", false);
IsSideObjective = element.GetAttributeBool("sideobjective", false);
RequireWreck = element.GetAttributeBool("requirewreck", false);
+ RequireRuin = element.GetAttributeBool("requireruin", false);
Commonness = element.GetAttributeInt("commonness", 1);
if (element.GetAttribute("difficulty") != null)
{
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs
index 23a121111..3d60228e2 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs
@@ -226,6 +226,7 @@ namespace Barotrauma
public void SetPurchasedItems(Dictionary> purchasedItems)
{
+ if (purchasedItems.Count == 0 && PurchasedItems.Count == 0) { return; }
PurchasedItems.Clear();
foreach (var entry in purchasedItems)
{
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs
index 3e33d0669..8a70b39e3 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs
@@ -64,7 +64,7 @@ namespace Barotrauma
if (reputationChange > 0f)
{
float reputationGainMultiplier = 1f;
- foreach (Character character in GameSession.GetSessionCrewCharacters())
+ foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
reputationGainMultiplier += character.GetStatValue(StatTypes.ReputationGainMultiplier);
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs
index abd84ef03..e10894b20 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Wallet.cs
@@ -6,6 +6,7 @@ namespace Barotrauma
{
internal readonly struct WalletChangedEvent
{
+ public readonly Option Owner;
public readonly Wallet Wallet;
public readonly WalletInfo Info;
public readonly WalletChangedData ChangedData;
@@ -15,6 +16,7 @@ namespace Barotrauma
Wallet = wallet;
Info = info;
ChangedData = changedData;
+ Owner = wallet.Owner;
}
}
@@ -109,6 +111,8 @@ namespace Barotrauma
// ReSharper disable ValueParameterNotUsed
internal sealed class InvalidWallet : Wallet
{
+ public InvalidWallet(): base(Option.None()) { }
+
public override int Balance
{
get => 0;
@@ -132,6 +136,8 @@ namespace Barotrauma
AttrubuteNameRewardDistribution = "rewarddistribution",
SaveElementName = "Wallet";
+ public readonly Option Owner;
+
private int balance;
public virtual int Balance
@@ -148,9 +154,12 @@ namespace Barotrauma
set => rewardDistribution = ClampRewardDistribution(value);
}
- public Wallet() { }
+ public Wallet(Option owner)
+ {
+ Owner = owner;
+ }
- public Wallet(XElement element)
+ public Wallet(Option owner, XElement element): this(owner)
{
balance = ClampBalance(element.GetAttributeInt(AttributeNameBalance, 0));
rewardDistribution = ClampBalance(element.GetAttributeInt(AttrubuteNameRewardDistribution, 0));
@@ -185,7 +194,7 @@ namespace Barotrauma
SettingsChanged(balanceChanged: Option.Some(-price), rewardChanged: Option.None());
}
- public void SetRewardDistrubiton(int value)
+ public void SetRewardDistribution(int value)
{
int oldValue = RewardDistribution;
RewardDistribution = value;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs
index 64bd03cb5..5dbf55fa0 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs
@@ -53,7 +53,7 @@ namespace Barotrauma
public int GetAddedMissionCount()
{
int count = 0;
- foreach (Character character in GameSession.GetSessionCrewCharacters())
+ foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
}
@@ -68,6 +68,15 @@ namespace Barotrauma
abstract partial class CampaignMode : GameMode
{
+ [NetworkSerialize]
+ public struct SaveInfo : INetSerializableStruct
+ {
+ public string FilePath;
+ public int SaveTime;
+ public string SubmarineName;
+ public string[] EnabledContentPackageNames;
+ }
+
public const int MaxMoney = int.MaxValue / 2; //about 1 billion
public const int InitialMoney = 8500;
@@ -180,13 +189,37 @@ namespace Barotrauma
protected CampaignMode(GameModePreset preset)
: base(preset)
{
- Bank = new Wallet
+ Bank = new Wallet(Option.None())
{
Balance = InitialMoney
};
CargoManager = new CargoManager(this);
MedicalClinic = new MedicalClinic(this);
+ Identifier messageIdentifier = new Identifier("money");
+
+#if CLIENT
+ OnMoneyChanged.RegisterOverwriteExisting(new Identifier("CampaignMoneyChangeNotification"), e =>
+ {
+ if (!(e.ChangedData.BalanceChanged is Some { Value: var changed })) { return; }
+
+ bool isGain = changed > 0;
+
+ Color clr = isGain ? GUIStyle.Yellow : GUIStyle.Red;
+
+ switch (e.Owner)
+ {
+ case Some { Value: var owner}:
+ owner.AddMessage(FormatMessage(), clr, playSound: Character.Controlled == owner, messageIdentifier, changed);
+ break;
+ case None _ when IsSinglePlayer:
+ Character.Controlled?.AddMessage(FormatMessage(), clr, playSound: true, messageIdentifier, changed);
+ break;
+ }
+
+ string FormatMessage() => TextManager.GetWithVariable(isGain ? "moneygainformat" : "moneyloseformat", "[money]", TextManager.FormatCurrency(Math.Abs(changed))).ToString();
+ });
+#endif
}
public virtual Wallet GetWallet(Client client = null)
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs
index 970f2eaff..781b6599e 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs
@@ -167,7 +167,7 @@ namespace Barotrauma
LoadStats(subElement);
break;
case Wallet.LowerCaseSaveElementName:
- Bank = new Wallet(subElement);
+ Bank = new Wallet(Option.None(), subElement);
break;
#if SERVER
case "savedexperiencepoints":
@@ -183,7 +183,7 @@ namespace Barotrauma
int oldMoney = element.GetAttributeInt("money", 0);
if (oldMoney > 0)
{
- Bank = new Wallet
+ Bank = new Wallet(Option.None())
{
Balance = oldMoney
};
diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs
index e6fc1f3e8..ac1b2e40b 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs
@@ -735,14 +735,40 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime);
- public static IEnumerable GetSessionCrewCharacters()
+ ///
+ /// Returns a list of crew characters currently in the game with a given filter.
+ ///
+ /// Character type filter
+ ///
+ ///
+ /// In singleplayer mode the CharacterType.Player returns the currently controlled player.
+ ///
+ public static ImmutableHashSet GetSessionCrewCharacters(CharacterType type)
{
+ if (!(GameMain.GameSession.CrewManager is { } crewManager)) { return ImmutableHashSet.Empty; }
+
+ IEnumerable players;
+ IEnumerable bots;
+ HashSet characters = new HashSet();
+
#if SERVER
- return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
-#else
- if (GameMain.GameSession?.CrewManager is null) { return Enumerable.Empty(); }
- return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null && !c.IsDead);
+ players = GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
+ bots = crewManager.GetCharacters().Where(c => !c.IsRemotePlayer);
+#elif CLIENT
+ players = crewManager.GetCharacters().Where(c => c.IsPlayer);
+ bots = crewManager.GetCharacters().Where(c => c.IsBot);
#endif
+ if (type.HasFlag(CharacterType.Bot))
+ {
+ foreach (Character bot in bots) { characters.Add(bot); }
+ }
+
+ if (type.HasFlag(CharacterType.Player))
+ {
+ foreach (Character player in players) { characters.Add(player); }
+ }
+
+ return characters.ToImmutableHashSet();
}
public void EndRound(string endMessage, List? traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
@@ -754,7 +780,7 @@ namespace Barotrauma
try
{
- ImmutableArray crewCharacters = GetSessionCrewCharacters().ToImmutableArray();
+ ImmutableHashSet crewCharacters = GetSessionCrewCharacters(CharacterType.Both);
int prevMoney = GetAmountOfMoney(crewCharacters);
@@ -898,7 +924,7 @@ namespace Barotrauma
}
}
- foreach (Character c in GetSessionCrewCharacters())
+ foreach (Character c in GetSessionCrewCharacters(CharacterType.Both))
{
foreach (var itemSelectedDuration in c.ItemSelectedDurations)
{
@@ -948,25 +974,25 @@ namespace Barotrauma
#endif
}
- public static bool IsCompatibleWithEnabledContentPackages(IList contentPackagePaths, out LocalizedString errorMsg)
+ public static bool IsCompatibleWithEnabledContentPackages(IList contentPackageNames, out LocalizedString errorMsg)
{
errorMsg = "";
//no known content packages, must be an older save file
- if (!contentPackagePaths.Any()) { return true; }
+ if (!contentPackageNames.Any()) { return true; }
List missingPackages = new List();
- foreach (string packagePath in contentPackagePaths)
+ foreach (string packageName in contentPackageNames)
{
- if (!ContentPackageManager.EnabledPackages.All.Any(cp => cp.Path == packagePath))
+ if (!ContentPackageManager.EnabledPackages.All.Any(cp => cp.NameMatches(packageName)))
{
- missingPackages.Add(packagePath);
+ missingPackages.Add(packageName);
}
}
List excessPackages = new List();
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
{
if (!cp.HasMultiplayerSyncedContent) { continue; }
- if (!contentPackagePaths.Any(p => p == cp.Path))
+ if (!contentPackageNames.Any(p => cp.NameMatches(p)))
{
excessPackages.Add(cp.Name);
}
@@ -976,9 +1002,9 @@ namespace Barotrauma
if (missingPackages.Count == 0 && missingPackages.Count == 0)
{
var enabledPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).ToImmutableArray();
- for (int i = 0; i < contentPackagePaths.Count && i < enabledPackages.Length; i++)
+ for (int i = 0; i < contentPackageNames.Count && i < enabledPackages.Length; i++)
{
- if (contentPackagePaths[i] != enabledPackages[i].Path)
+ if (!enabledPackages[i].NameMatches(contentPackageNames[i]))
{
orderMismatch = true;
break;
@@ -1009,7 +1035,7 @@ namespace Barotrauma
if (orderMismatch)
{
if (!errorMsg.IsNullOrEmpty()) { errorMsg += "\n"; }
- errorMsg += TextManager.GetWithVariable("campaignmode.contentpackageordermismatch", "[loadorder]", string.Join(", ", contentPackagePaths));
+ errorMsg += TextManager.GetWithVariable("campaignmode.contentpackageordermismatch", "[loadorder]", string.Join(", ", contentPackageNames));
}
return false;
@@ -1040,8 +1066,8 @@ namespace Barotrauma
}
}
if (Map != null) { rootElement.Add(new XAttribute("mapseed", Map.Seed)); }
- rootElement.Add(new XAttribute("selectedcontentpackages",
- string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Path))));
+ rootElement.Add(new XAttribute("selectedcontentpackagenames",
+ string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Name.Replace("|", @"\|")))));
((CampaignMode)GameMode).Save(doc.Root);
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs
index 7d03844eb..296277c9d 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Controller.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
-using Barotrauma.Extensions;
namespace Barotrauma.Items.Components
{
@@ -13,6 +12,7 @@ namespace Barotrauma.Items.Components
{
[Editable]
public LimbType LimbType { get; set; }
+
[Editable]
public Vector2 Position { get; set; }
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
partial class Controller : ItemComponent, IServerSerializable
{
//where the limbs of the user should be positioned when using the controller
- private readonly List limbPositions;
+ private readonly List limbPositions = new List();
private Direction dir;
@@ -117,38 +117,9 @@ namespace Barotrauma.Items.Components
public Controller(Item item, ContentXElement element)
: base(item, element)
{
- limbPositions = new List();
-
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
-
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
-
- foreach (var subElement in element.Elements())
- {
- if (subElement.Name != "limbposition") { continue; }
- string limbStr = subElement.GetAttributeString("limb", "");
- if (!Enum.TryParse(subElement.GetAttribute("limb").Value, out LimbType limbType))
- {
- DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
- }
- else
- {
- LimbPos limbPos = new LimbPos(limbType,
- subElement.GetAttributeVector2("position", Vector2.Zero),
- subElement.GetAttributeBool("allowusinglimb", false));
- limbPositions.Add(limbPos);
- if (!limbPos.AllowUsingLimb)
- {
- if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
- limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
- {
- AllowAiming = false;
- }
- }
- }
-
- }
-
+ LoadLimbPositions(element);
IsActive = true;
}
@@ -529,5 +500,63 @@ namespace Barotrauma.Items.Components
}
partial void HideHUDs(bool value);
+
+ public override XElement Save(XElement parentElement)
+ {
+ return SaveLimbPositions(base.Save(parentElement));
+ }
+
+ public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
+ {
+ base.Load(componentElement, usePrefabValues, idRemap);
+ if (GameMain.GameSession?.GameMode?.Preset == GameModePreset.TestMode)
+ {
+ LoadLimbPositions(componentElement);
+ }
+ }
+
+ private XElement SaveLimbPositions(XElement element)
+ {
+ if (Screen.Selected == GameMain.SubEditorScreen)
+ {
+ foreach (var limbPos in limbPositions)
+ {
+ element.Add(new XElement("limbposition",
+ new XAttribute("limb", limbPos.LimbType),
+ new XAttribute("position", XMLExtensions.Vector2ToString(limbPos.Position)),
+ new XAttribute("allowusinglimb", limbPos.AllowUsingLimb)));
+ }
+ }
+ return element;
+ }
+
+ private void LoadLimbPositions(XElement element)
+ {
+ limbPositions.Clear();
+ foreach (var subElement in element.Elements())
+ {
+ if (subElement.Name != "limbposition") { continue; }
+ string limbStr = subElement.GetAttributeString("limb", "");
+ if (!Enum.TryParse(subElement.GetAttribute("limb").Value, out LimbType limbType))
+ {
+ DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
+ }
+ else
+ {
+ LimbPos limbPos = new LimbPos(limbType,
+ subElement.GetAttributeVector2("position", Vector2.Zero),
+ subElement.GetAttributeBool("allowusinglimb", false));
+ limbPositions.Add(limbPos);
+ if (!limbPos.AllowUsingLimb)
+ {
+ if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
+ limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
+ {
+ AllowAiming = false;
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs
index 27b9ae460..ee10aee6e 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs
@@ -80,6 +80,8 @@ namespace Barotrauma.Items.Components
private float progressState;
+ private readonly Dictionary fabricationLimits = new Dictionary();
+
public Fabricator(Item item, ContentXElement element)
: base(item, element)
{
@@ -105,6 +107,10 @@ namespace Barotrauma.Items.Components
}
}
fabricationRecipes.Add(recipe.RecipeHash, recipe);
+ if (recipe.FabricationLimitMax >= 0)
+ {
+ fabricationLimits.Add(recipe.RecipeHash, Rand.Range(recipe.FabricationLimitMin, recipe.FabricationLimitMax + 1));
+ }
}
}
this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
@@ -244,7 +250,7 @@ namespace Barotrauma.Items.Components
itemList.Enabled = true;
if (activateButton != null)
{
- activateButton.Text = TextManager.Get("FabricatorCreate");
+ activateButton.Text = TextManager.Get(CreateButtonText);
}
#endif
fabricatedItem = null;
@@ -400,8 +406,22 @@ namespace Barotrauma.Items.Components
quality = GetFabricatedItemQuality(fabricatedItem, user);
}
+ int amount = (int)fabricationitemAmount.Value;
+ if (fabricationLimits.ContainsKey(fabricatedItem.RecipeHash))
+ {
+ if (amount > fabricationLimits[fabricatedItem.RecipeHash])
+ {
+ amount = fabricationLimits[fabricatedItem.RecipeHash];
+ fabricationLimits[fabricatedItem.RecipeHash] = 0;
+ }
+ else
+ {
+ fabricationLimits[fabricatedItem.RecipeHash] -= amount;
+ }
+ }
+
var tempUser = user;
- for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
+ for (int i = 0; i < amount; i++)
{
float outCondition = fabricatedItem.OutCondition;
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
@@ -535,6 +555,11 @@ namespace Barotrauma.Items.Components
}
}
+ if (fabricationLimits.TryGetValue(fabricableItem.RecipeHash, out int amount) && amount <= 0)
+ {
+ return false;
+ }
+
return fabricableItem.RequiredItems.All(requiredItem =>
{
int availablePrefabsAmount = 0;
@@ -698,7 +723,6 @@ namespace Barotrauma.Items.Components
componentElement.Add(new XAttribute("fabricateditemidentifier", fabricatedItem.TargetItem.Identifier));
componentElement.Add(new XAttribute("savedtimeuntilready", timeUntilReady.ToString("G", CultureInfo.InvariantCulture)));
componentElement.Add(new XAttribute("savedrequiredtime", requiredTime.ToString("G", CultureInfo.InvariantCulture)));
-
}
return componentElement;
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs
index 93c68e6ac..411627eef 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -114,7 +115,7 @@ namespace Barotrauma
private readonly Quality qualityComponent;
- private readonly Queue impactQueue = new Queue();
+ private readonly ConcurrentQueue impactQueue = new ConcurrentQueue();
//a dictionary containing lists of the status effects in all the components of the item
private readonly bool[] hasStatusEffectsOfType;
@@ -1695,9 +1696,8 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
- while (impactQueue.Count > 0)
+ while (impactQueue.TryDequeue(out float impact))
{
- float impact = impactQueue.Dequeue();
HandleCollision(impact);
}
@@ -1933,10 +1933,7 @@ namespace Barotrauma
if (contact.FixtureA.Body == f1.Body) { normal = -normal; }
float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
- lock (impactQueue)
- {
- impactQueue.Enqueue(impact);
- }
+ impactQueue.Enqueue(impact);
return true;
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs
index 33d5b6289..8e25476cc 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs
@@ -119,6 +119,11 @@ namespace Barotrauma
public readonly uint RecipeHash;
public readonly int Amount;
+ ///
+ /// How many of this item the fabricator can create (< 0 = unlimited)
+ ///
+ public readonly int FabricationLimitMin, FabricationLimitMax;
+
public FabricationRecipe(XElement element, Identifier itemPrefab)
{
TargetItemPrefabIdentifier = itemPrefab;
@@ -141,6 +146,10 @@ namespace Barotrauma
RequiresRecipe = element.GetAttributeBool("requiresrecipe", false);
Amount = element.GetAttributeInt("amount", 1);
+ int limitDefault = element.GetAttributeInt("fabricationlimit", -1);
+ FabricationLimitMin = element.GetAttributeInt(nameof(FabricationLimitMin), limitDefault);
+ FabricationLimitMax = element.GetAttributeInt(nameof(FabricationLimitMax), limitDefault);
+
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
@@ -973,7 +982,11 @@ namespace Barotrauma
public int? GetMinPrice()
{
- int? minPrice = StorePrices.Values.Min(p => p.Price);
+ int? minPrice = null;
+ if (StorePrices != null && StorePrices.Any())
+ {
+ minPrice = StorePrices.Values.Min(p => p.Price);
+ }
if (minPrice.HasValue)
{
if (DefaultPrice != null)
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs
index f184f08f4..4db3d907b 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs
@@ -831,7 +831,13 @@ namespace Barotrauma
}
List ruinPositions = new List();
- for (int i = 0; i < GenerationParams.RuinCount; i++)
+ int ruinCount = GenerationParams.RuinCount;
+ if (GameMain.GameSession?.GameMode?.Missions.Any(m => m.Prefab.RequireRuin) ?? false)
+ {
+ ruinCount = Math.Max(ruinCount, 1);
+ }
+
+ for (int i = 0; i < ruinCount; i++)
{
Point ruinSize = new Point(5000);
int limitLeft = Math.Max(startPosition.X, ruinSize.X / 2);
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelGenerationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelGenerationParams.cs
index d1fc85553..cb6640f49 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelGenerationParams.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelGenerationParams.cs
@@ -387,14 +387,14 @@ namespace Barotrauma
set;
}
- [Serialize(3, IsPropertySaveable.Yes, description: "Minimum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
+ [Serialize(10, IsPropertySaveable.Yes, description: "Minimum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
public int AbyssResourceClustersMin
{
get;
set;
}
- [Serialize(20, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
+ [Serialize(50, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
public int AbyssResourceClustersMax
{
get;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs
index 4b77d08fa..84d2510c2 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs
@@ -257,7 +257,7 @@ namespace Barotrauma
}
DailySpecials.Clear();
int extraSpecialSalesCount = Location.GetExtraSpecialSalesCount();
- for (int i = 0; i < DailySpecialsCount + extraSpecialSalesCount; i++)
+ for (int i = 0; i < Location.DailySpecialsCount + extraSpecialSalesCount; i++)
{
if (availableStock.None()) { break; }
var item = ToolBox.SelectWeightedRandom(availableStock.Keys.ToList(), availableStock.Values.ToList(), Rand.RandSync.Unsynced);
@@ -266,7 +266,7 @@ namespace Barotrauma
availableStock.Remove(item);
}
RequestedGoods.Clear();
- for (int i = 0; i < RequestedGoodsCount; i++)
+ for (int i = 0; i < Location.RequestedGoodsCount; i++)
{
var item = ItemPrefab.Prefabs.GetRandom(p =>
p.CanBeSold && !RequestedGoods.Contains(p) &&
@@ -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 const int DailySpecialsCount = 3;
- private const int RequestedGoodsCount = 3;
+ private int DailySpecialsCount => Type.DailySpecialsCount;
+ private int RequestedGoodsCount => Type.RequestedGoodsCount;
private int StepsSinceSpecialsUpdated { get; set; }
public HashSet StoreIdentifiers { get; } = new HashSet();
@@ -1226,7 +1226,7 @@ namespace Barotrauma
public int GetExtraSpecialSalesCount()
{
- var characters = GameSession.GetSessionCrewCharacters();
+ var characters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
if (!characters.Any()) { return 0; }
return characters.Max(c => (int)c.GetStatValue(StatTypes.ExtraSpecialSalesCount));
}
@@ -1252,7 +1252,7 @@ namespace Barotrauma
Discovered = true;
if (checkTalents)
{
- GameSession.GetSessionCrewCharacters().ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new AbilityLocation(this)));
+ GameSession.GetSessionCrewCharacters(CharacterType.Both).ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new AbilityLocation(this)));
}
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs
index 2bfa64c65..e81e2795d 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs
@@ -1,13 +1,11 @@
-using Microsoft.Xna.Framework;
+using Barotrauma.IO;
+using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
-using Barotrauma.IO;
using System.Linq;
-using System.Xml.Linq;
-using Barotrauma.Extensions;
-using System.Collections.Immutable;
namespace Barotrauma
{
@@ -21,8 +19,9 @@ namespace Barotrauma
//
private readonly ImmutableArray<(Identifier Name, float Commonness)> hireableJobs;
private readonly float totalHireableWeight;
-
- public Dictionary CommonnessPerZone = new Dictionary();
+
+ public readonly Dictionary CommonnessPerZone = new Dictionary();
+ public readonly Dictionary MinCountPerZone = new Dictionary();
public readonly LocalizedString Name;
@@ -65,7 +64,7 @@ namespace Barotrauma
get;
private set;
}
-
+
public string ReplaceInRadiation { get; }
public Sprite Sprite { get; private set; }
@@ -86,6 +85,8 @@ namespace Barotrauma
/// In percentages
///
public int StorePriceModifierRange { get; } = 5;
+ public int DailySpecialsCount { get; } = 1;
+ public int RequestedGoodsCount { get; } = 1;
public List StoreBalanceStatuses { get; } = new List()
{
@@ -144,7 +145,7 @@ namespace Barotrauma
names = new List() { "Name file not found" };
}
- string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", new string[] { "" });
+ string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", Array.Empty());
foreach (string commonnessPerZoneStr in commonnessPerZoneStrs)
{
string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(':');
@@ -152,12 +153,26 @@ namespace Barotrauma
!int.TryParse(splitCommonnessPerZone[0].Trim(), out int zoneIndex) ||
!float.TryParse(splitCommonnessPerZone[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float zoneCommonness))
{
- DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone0index: zone0commonness, zone1index: zone1commonness\"");
+ DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone1index: zone1commonness, zone2index: zone2commonness\"");
break;
}
CommonnessPerZone[zoneIndex] = zoneCommonness;
}
+ string[] minCountPerZoneStrs = element.GetAttributeStringArray("mincountperzone", Array.Empty());
+ foreach (string minCountPerZoneStr in minCountPerZoneStrs)
+ {
+ string[] splitMinCountPerZone = minCountPerZoneStr.Split(':');
+ if (splitMinCountPerZone.Length != 2 ||
+ !int.TryParse(splitMinCountPerZone[0].Trim(), out int zoneIndex) ||
+ !int.TryParse(splitMinCountPerZone[1].Trim(), out int minCount))
+ {
+ DebugConsole.ThrowError("Failed to read minimum count values for location type \"" + Identifier + "\" - minimum counts should be given in the format \"zone1index: zone1mincount, zone2index: zone2mincount\"");
+ break;
+ }
+ MinCountPerZone[zoneIndex] = minCount;
+ }
+
var hireableJobs = new List<(Identifier, float)>();
foreach (var subElement in element.Elements())
{
@@ -205,6 +220,8 @@ namespace Barotrauma
StoreBalanceStatuses.Add(new StoreBalanceStatus(percentage, modifier, color));
}
}
+ DailySpecialsCount = subElement.GetAttributeInt("dailyspecialscount", DailySpecialsCount);
+ RequestedGoodsCount = subElement.GetAttributeInt("requestedgoodscount", RequestedGoodsCount);
break;
}
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs
index 0a793e304..478350b10 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs
@@ -218,16 +218,24 @@ namespace Barotrauma
foreach (Location location in Locations)
{
- if (location.Type.Identifier != "city" &&
- location.Type.Identifier != "outpost")
- {
- continue;
- }
+ if (location.Type.Identifier != "outpost") { continue; }
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
{
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
}
}
+ //if no outpost was found (using a mod that replaces the outpost location type?), find any type of outpost
+ if (CurrentLocation == null)
+ {
+ foreach (Location location in Locations)
+ {
+ if (!location.Type.HasOutpost) { continue; }
+ if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
+ {
+ CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
+ }
+ }
+ }
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
CurrentLocation.Discover(true);
@@ -273,6 +281,7 @@ namespace Barotrauma
}
voronoiSites.Clear();
+ Dictionary> locationsPerZone = new Dictionary>();
foreach (GraphEdge edge in edges)
{
if (edge.Point1 == edge.Point2) { continue; }
@@ -301,7 +310,24 @@ namespace Barotrauma
Vector2 position = points[positionIndex];
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) { position = points[1 - positionIndex]; }
int zone = GetZoneIndex(position.X);
- newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.ServerAndClient), requireOutpost: false, existingLocations: Locations);
+ if (!locationsPerZone.ContainsKey(zone))
+ {
+ locationsPerZone[zone] = new List();
+ }
+
+ LocationType forceLocationType = null;
+ foreach (LocationType locationType in LocationType.Prefabs.OrderBy(lt => lt.Identifier))
+ {
+ if (locationType.MinCountPerZone.TryGetValue(zone, out int minCount) && locationsPerZone[zone].Count(l => l.Type == locationType) < minCount)
+ {
+ forceLocationType = locationType;
+ break;
+ }
+ }
+
+ newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.ServerAndClient),
+ requireOutpost: false, forceLocationType: forceLocationType, existingLocations: Locations);
+ locationsPerZone[zone].Add(newLocations[i]);
Locations.Add(newLocations[i]);
}
@@ -448,8 +474,7 @@ namespace Barotrauma
Connections[i].Locations[1];
if (!leftMostLocation.Type.HasOutpost || leftMostLocation.Type.Identifier == "abandoned")
{
- #warning TODO: determinism?
- leftMostLocation.ChangeType(LocationType.Prefabs.First(lt => lt.HasOutpost && lt.Identifier != "abandoned"));
+ leftMostLocation.ChangeType(LocationType.Prefabs.OrderBy(lt => lt.Identifier).First(lt => lt.HasOutpost && lt.Identifier != "abandoned"));
}
leftMostLocation.IsGateBetweenBiomes = true;
Connections[i].Locked = true;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs
index 336462b62..594adb116 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs
@@ -752,6 +752,7 @@ namespace Barotrauma
bool solutionFound = false;
foreach (PlacedModule module in movableModules)
{
+ if (module.ThisGap.ConnectedDoor == null && module.PreviousGap.ConnectedDoor == null) { continue; }
Vector2 moveDir = GetMoveDir(module.ThisGapPosition);
Vector2 moveStep = moveDir * 50.0f;
Vector2 currentMove = Vector2.Zero;
@@ -1093,6 +1094,10 @@ namespace Barotrauma
}
thisWayPoint.Remove();
}
+ else
+ {
+ DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {GetOpposingGapPosition(module.ThisGapPosition).ToString().ToLower()} gap of the module \"{module.PreviousModule.Info.Name}\".");
+ }
gapToRemove.ConnectedDoor?.Item.Remove();
if (hallwayLength <= 1.0f) { gapToRemove?.Remove(); }
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs
index 8dde595c0..c4e2cf5fa 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineInfo.cs
@@ -28,8 +28,6 @@ namespace Barotrauma
partial class SubmarineInfo : IDisposable
{
- public const string SavePath = "Submarines";
-
private static List savedSubmarines = new List();
public static IEnumerable SavedSubmarines => savedSubmarines;
@@ -578,58 +576,14 @@ namespace Barotrauma
if (File.Exists(savedSubmarines[i].FilePath))
{
bool isDownloadedSub = Path.GetFullPath(Path.GetDirectoryName(savedSubmarines[i].FilePath)) == Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
- bool isInSubmarinesFolder = Path.GetFullPath(Path.GetDirectoryName(savedSubmarines[i].FilePath)) == Path.GetFullPath(SavePath);
bool isInContentPackage = contentPackageSubs.Any(f => f.Path == savedSubmarines[i].FilePath);
if (isDownloadedSub) { continue; }
- if (savedSubmarines[i].LastModifiedTime == File.GetLastWriteTime(savedSubmarines[i].FilePath) && (isInSubmarinesFolder || isInContentPackage)) { continue; }
+ if (savedSubmarines[i].LastModifiedTime == File.GetLastWriteTime(savedSubmarines[i].FilePath) && isInContentPackage) { continue; }
}
savedSubmarines[i].Dispose();
}
- if (!Directory.Exists(SavePath))
- {
- try
- {
- Directory.CreateDirectory(SavePath);
- }
- catch (Exception e)
- {
- DebugConsole.ThrowError("Directory \"" + SavePath + "\" not found and creating the directory failed.", e);
- return;
- }
- }
-
- List filePaths;
- string[] subDirectories;
-
- try
- {
- filePaths = Directory.GetFiles(SavePath).ToList();
- subDirectories = Directory.GetDirectories(SavePath).Where(s =>
- {
- DirectoryInfo dir = new DirectoryInfo(s);
- return !dir.Attributes.HasFlag(System.IO.FileAttributes.Hidden) && !dir.Name.StartsWith(".");
- }).ToArray();
- }
- catch (Exception e)
- {
- DebugConsole.ThrowError("Couldn't open directory \"" + SavePath + "\"!", e);
- return;
- }
-
- foreach (string subDirectory in subDirectories)
- {
- try
- {
- filePaths.AddRange(Directory.GetFiles(subDirectory).ToList());
- }
- catch (Exception e)
- {
- DebugConsole.ThrowError("Couldn't open subdirectory \"" + subDirectory + "\"!", e);
- return;
- }
- }
-
+ List filePaths = new List();
foreach (BaseSubFile subFile in contentPackageSubs)
{
if (!filePaths.Any(fp => fp == subFile.Path))
@@ -643,34 +597,7 @@ namespace Barotrauma
foreach (string path in filePaths)
{
var subInfo = new SubmarineInfo(path);
- if (subInfo.IsFileCorrupted)
- {
-#if CLIENT
- if (DebugConsole.IsOpen) { DebugConsole.Toggle(); }
- var deleteSubPrompt = new GUIMessageBox(
- TextManager.Get("Error"),
- TextManager.GetWithVariable("SubLoadError", "[subname]", subInfo.Name) + "\n" +
- TextManager.GetWithVariable("DeleteFileVerification", "[filename]", subInfo.Name),
- new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
-
- string filePath = path;
- deleteSubPrompt.Buttons[0].OnClicked += (btn, userdata) =>
- {
- try
- {
- File.Delete(filePath);
- }
- catch (Exception e)
- {
- DebugConsole.ThrowError($"Failed to delete file \"{filePath}\".", e);
- }
- deleteSubPrompt.Close();
- return true;
- };
- deleteSubPrompt.Buttons[1].OnClicked += deleteSubPrompt.Close;
-#endif
- }
- else
+ if (!subInfo.IsFileCorrupted)
{
savedSubmarines.Add(subInfo);
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs
index 0c3b25d2f..d18e54eef 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ClientPermissions.cs
@@ -22,11 +22,12 @@ namespace Barotrauma.Networking
ManageSettings = 0x200,
ManagePermissions = 0x400,
KarmaImmunity = 0x800,
- BuyItems = 0x1000,
+ ManageMoney = 0x1000,
SellInventoryItems = 0x2000,
SellSubItems = 0x4000,
- CampaignStore = 0x8000,
- All = 0xFFFF
+ ManageMap = 0x8000,
+ ManageHires = 0x10000,
+ All = 0x1FFFF
}
class PermissionPreset
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs
index e405ecb4f..b8c2d6419 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs
@@ -116,7 +116,8 @@ namespace Barotrauma.Networking
StartRound,
PurchaseAndSwitchSub,
PurchaseSub,
- SwitchSub
+ SwitchSub,
+ TransferMoney
}
public enum ReadyCheckState
@@ -179,11 +180,11 @@ namespace Barotrauma.Networking
protected ServerSettings serverSettings;
+ public Voting Voting { get; protected set; }
+
protected TimeSpan updateInterval;
protected DateTime updateTimer;
- public int EndVoteCount, EndVoteMax, SubmarineVoteYesCount, SubmarineVoteNoCount, SubmarineVoteMax;
-
protected bool gameStarted;
protected RespawnManager respawnManager;
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs
index f844f406a..c182cfdf5 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs
@@ -278,8 +278,6 @@ namespace Barotrauma.Networking
{
ServerLog = new ServerLog(serverName);
- Voting = new Voting();
-
Whitelist = new WhiteList();
BanList = new BanList();
@@ -378,8 +376,6 @@ namespace Barotrauma.Networking
public ServerLog ServerLog;
- public Voting Voting;
-
public Dictionary MonsterEnabled { get; private set; }
public const int MaxExtraCargoItemsOfType = 10;
@@ -577,34 +573,20 @@ namespace Barotrauma.Networking
[Serialize(true, IsPropertySaveable.Yes)]
public bool AllowVoteKick
{
- get
- {
- return Voting.AllowVoteKick;
- }
- set
- {
- Voting.AllowVoteKick = value;
- }
+ get; set;
}
[Serialize(true, IsPropertySaveable.Yes)]
public bool AllowEndVoting
{
- get
- {
- return Voting.AllowEndVoting;
- }
- set
- {
- Voting.AllowEndVoting = value;
- }
+ get; set;
}
private bool allowRespawn;
[Serialize(true, IsPropertySaveable.Yes)]
public bool AllowRespawn
{
- get { return allowRespawn; ; }
+ get { return allowRespawn; }
set
{
if (allowRespawn == value) { return; }
@@ -779,7 +761,7 @@ namespace Barotrauma.Networking
set
{
subSelectionMode = value;
- Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
+ AllowSubVoting = subSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
}
}
@@ -792,7 +774,7 @@ namespace Barotrauma.Networking
set
{
modeSelectionMode = value;
- Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
+ AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
ServerDetailsChanged = true;
}
}
@@ -807,14 +789,14 @@ namespace Barotrauma.Networking
}
[Serialize(0.6f, IsPropertySaveable.Yes)]
- public float SubmarineVoteRequiredRatio
+ public float VoteRequiredRatio
{
get;
private set;
}
[Serialize(30f, IsPropertySaveable.Yes)]
- public float SubmarineVoteTimeout
+ public float VoteTimeout
{
get;
private set;
@@ -928,6 +910,59 @@ namespace Barotrauma.Networking
set { maxMissionCount = MathHelper.Clamp(value, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit); }
}
+ private bool allowSubVoting;
+ //Don't serialize: the value is set based on SubSelectionMode
+ public bool AllowSubVoting
+ {
+ get { return allowSubVoting; }
+ set
+ {
+ if (value == allowSubVoting) { return; }
+ allowSubVoting = value;
+#if CLIENT
+ GameMain.NetLobbyScreen.SubList.Enabled = value ||
+ (GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectSub));
+ var subVotesLabel = GameMain.NetLobbyScreen.Frame.FindChild("subvotes", true) as GUITextBlock;
+ subVotesLabel.Visible = value;
+ var subVisButton = GameMain.NetLobbyScreen.SubVisibilityButton;
+ subVisButton.RectTransform.AbsoluteOffset
+ = new Point(value ? (int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
+
+ GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Sub);
+ GameMain.NetLobbyScreen.SubList.Deselect();
+#endif
+ }
+ }
+
+ private bool allowModeVoting;
+ //Don't serialize: the value is set based on ModeSelectionMode
+ public bool AllowModeVoting
+ {
+ get { return allowModeVoting; }
+ set
+ {
+ if (value == allowModeVoting) { return; }
+ allowModeVoting = value;
+#if CLIENT
+ GameMain.NetLobbyScreen.ModeList.Enabled =
+ value ||
+ (GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectMode));
+ GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
+ // Disable modes that cannot be voted on
+ foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
+ {
+ if (guiComponent is GUIFrame frame)
+ {
+ frame.CanBeFocused = !allowModeVoting || ((GameModePreset)frame.UserData).Votable;
+ }
+ }
+ GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Mode);
+ GameMain.NetLobbyScreen.ModeList.Deselect();
+#endif
+ }
+ }
+
+
public void SetPassword(string password)
{
if (string.IsNullOrEmpty(password))
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Voting.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Voting.cs
index ecab1d04a..5fd149b62 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Voting.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Voting.cs
@@ -1,19 +1,11 @@
using Barotrauma.Networking;
+using System;
using System.Collections.Generic;
-using System.Linq;
namespace Barotrauma
{
partial class Voting
{
- private bool allowSubVoting, allowModeVoting;
-
- public bool AllowVoteKick = true;
-
- public bool AllowEndVoting = true;
-
- public bool VoteRunning = false;
-
public enum VoteState { None = 0, Started = 1, Running = 2, Passed = 3, Failed = 4 };
private IReadOnlyDictionary GetVoteCounts(VoteType voteType, List voters)
@@ -39,12 +31,12 @@ namespace Barotrauma
public T HighestVoted(VoteType voteType, List voters)
{
- if (voteType == VoteType.Sub && !AllowSubVoting) return default(T);
- if (voteType == VoteType.Mode && !AllowModeVoting) return default(T);
+ if (voteType == VoteType.Sub && !GameMain.NetworkMember.ServerSettings.AllowSubVoting) { return default; }
+ if (voteType == VoteType.Mode && !GameMain.NetworkMember.ServerSettings.AllowModeVoting) { return default; }
IReadOnlyDictionary voteList = GetVoteCounts(voteType, voters);
- T selected = default(T);
+ T selected = default;
int highestVotes = 0;
foreach (KeyValuePair votable in voteList)
{
@@ -71,11 +63,13 @@ namespace Barotrauma
{
client.ResetVotes();
}
-
- GameMain.NetworkMember.EndVoteCount = 0;
- GameMain.NetworkMember.EndVoteMax = 0;
-
#if CLIENT
+ foreach (VoteType voteType in Enum.GetValues(typeof(VoteType)))
+ {
+ SetVoteCountYes(voteType, 0);
+ SetVoteCountNo(voteType, 0);
+ SetVoteCountMax(voteType, 0);
+ }
UpdateVoteTexts(connectedClients, VoteType.Mode);
UpdateVoteTexts(connectedClients, VoteType.Sub);
#endif
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs
index 9684fbe89..e44c75dd2 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Steam/Workshop.cs
@@ -348,6 +348,14 @@ namespace Barotrauma.Steam
string val = attribute.Value.CleanUpPathCrossPlatform(correctFilenameCase: false);
+ //Handle mods that have been mangled by pre-modding-refactor
+ //copying of post-modding-refactor mods (what a clusterfuck)
+ int modDirStrIndex = val.IndexOf(ContentPath.ModDirStr, StringComparison.OrdinalIgnoreCase);
+ if (modDirStrIndex >= 0)
+ {
+ val = val[modDirStrIndex..];
+ }
+
//Handle really old mods (0.9.0.4-era) that might be structured as
//%ModDir%/Mods/[NAME]/[RESOURCE]
string fullSrcPath = Path.Combine(fileListDir, val).CleanUpPath();
@@ -418,7 +426,7 @@ namespace Barotrauma.Steam
File.Copy(from, to, overwrite: true);
}
- private static async Task CopyDirectory(string fileListDir, string modName, string from, string to)
+ public static async Task CopyDirectory(string fileListDir, string modName, string from, string to)
{
from = Path.GetFullPath(from); to = Path.GetFullPath(to);
Directory.CreateDirectory(to);
diff --git a/Barotrauma/BarotraumaShared/SharedSource/SteamAchievementManager.cs b/Barotrauma/BarotraumaShared/SharedSource/SteamAchievementManager.cs
index 3963df1ca..9f3239e41 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/SteamAchievementManager.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/SteamAchievementManager.cs
@@ -79,10 +79,10 @@ namespace Barotrauma
{
roundData.EnteredCrushDepth.Add(c);
}
- else if (Level.Loaded.GetRealWorldDepth(c.WorldPosition.Y) < Level.Loaded.RealWorldCrushDepth * 0.5f)
+ else if (Level.Loaded.GetRealWorldDepth(c.WorldPosition.Y) < Level.Loaded.RealWorldCrushDepth - 500.0f)
{
//all characters that have entered crush depth and are still alive get an achievement
- if (roundData.EnteredCrushDepth.Contains(c)) UnlockAchievement(c, "survivecrushdepth".ToIdentifier());
+ if (roundData.EnteredCrushDepth.Contains(c)) { UnlockAchievement(c, "survivecrushdepth".ToIdentifier()); }
}
}
}
@@ -426,7 +426,7 @@ namespace Barotrauma
!c.IsDead &&
c.TeamID != CharacterTeamType.FriendlyNPC &&
!(c.AIController is EnemyAIController) &&
- (c.Submarine == gameSession.Submarine || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
+ (c.Submarine == gameSession.Submarine || gameSession.Submarine.GetConnectedSubs().Contains(c.Submarine) || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
if (charactersInSub.Count == 1)
{
@@ -454,6 +454,10 @@ namespace Barotrauma
}
foreach (Character character in charactersInSub)
{
+ if (roundData.EnteredCrushDepth.Contains(character))
+ {
+ UnlockAchievement(character, "survivecrushdepth".ToIdentifier());
+ }
if (character.Info.Job == null) { continue; }
UnlockAchievement(character, $"{character.Info.Job.Prefab.Identifier}round".ToIdentifier());
}
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Md5Hash.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/Md5Hash.cs
similarity index 100%
rename from Barotrauma/BarotraumaShared/SharedSource/Map/Md5Hash.cs
rename to Barotrauma/BarotraumaShared/SharedSource/Utils/Md5Hash.cs
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/NamedEvent.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/NamedEvent.cs
index 7b46b9467..9f0a2f711 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Utils/NamedEvent.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/NamedEvent.cs
@@ -37,6 +37,12 @@ namespace Barotrauma
events.Remove(identifier);
}
+ public void TryDeregister(Identifier identifier)
+ {
+ if (!HasEvent(identifier)) { return; }
+ Deregister(identifier);
+ }
+
public bool HasEvent(Identifier identifier) => events.ContainsKey(identifier);
public void Invoke(T data)
diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs
index 4101d9513..e0b919969 100644
--- a/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs
+++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/SaveUtil.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Xml.Linq;
using Steamworks.Data;
using Color = Microsoft.Xna.Framework.Color;
+using System.Text.RegularExpressions;
namespace Barotrauma
{
@@ -227,7 +228,7 @@ namespace Barotrauma
return Path.Combine(folder, saveName);
}
- public static IEnumerable GetSaveFiles(SaveType saveType, bool includeInCompatible = true)
+ public static IReadOnlyList GetSaveFiles(SaveType saveType, bool includeInCompatible = true)
{
string folder = saveType == SaveType.Singleplayer ? SaveFolder : MultiplayerSaveFolder;
if (!Directory.Exists(folder))
@@ -250,18 +251,61 @@ namespace Barotrauma
files.AddRange(Directory.GetFiles(legacyFolder, "*.save", System.IO.SearchOption.TopDirectoryOnly));
}
- if (!includeInCompatible)
+ List saveInfos = new List();
+ foreach (string file in files)
{
- for (int i = files.Count - 1; i >= 0; i--)
+ XDocument doc = LoadGameSessionDoc(file);
+ if (!includeInCompatible && !IsSaveFileCompatible(doc))
{
- XDocument doc = LoadGameSessionDoc(files[i]);
- if (!IsSaveFileCompatible(doc))
+ continue;
+ }
+ if (doc?.Root == null)
+ {
+ saveInfos.Add(new CampaignMode.SaveInfo()
{
- files.RemoveAt(i);
+ FilePath = file
+ });
+ }
+ else
+ {
+ List enabledContentPackageNames = new List();
+
+ //backwards compatibility
+ string enabledContentPackagePathsStr = doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", string.Empty);
+ foreach (string packagePath in enabledContentPackagePathsStr.Split('|'))
+ {
+ if (string.IsNullOrEmpty(packagePath)) { continue; }
+ //change paths to names
+ string fileName = Path.GetFileNameWithoutExtension(packagePath);
+ if (fileName == "filelist")
+ {
+ enabledContentPackageNames.Add(Path.GetFileName(Path.GetDirectoryName(packagePath)));
+ }
+ else
+ {
+ enabledContentPackageNames.Add(fileName);
+ }
}
+
+ string enabledContentPackageNamesStr = doc.Root.GetAttributeStringUnrestricted("selectedcontentpackagenames", string.Empty);
+ //split on pipes, excluding pipes preceded by \
+ foreach (string packageName in Regex.Split(enabledContentPackageNamesStr, @"(?