Unstable 0.17.5.0

This commit is contained in:
Markus Isberg
2022-03-30 01:20:59 +09:00
parent c1b8e5a341
commit 44ded0225a
88 changed files with 2033 additions and 1430 deletions
@@ -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)
{
@@ -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
{
/// <summary>
/// Class dedicated to transitioning away from the old, shitty
/// Mods + Submarines folders to the new LocalMods folder
/// </summary>
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<string, GUITickBox> pathTickboxMap = new Dictionary<string, GUITickBox>();
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<LocalizedString>());
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<string> FilePaths;
public OldSubs(IReadOnlyList<string> 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<string>();
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<string, GUITickBox> 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<SubmarineFile>(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);
}
}
}
@@ -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--)
@@ -22,7 +22,7 @@ namespace Barotrauma
private GUIButton clearAllButton;
private List<CharacterInfo> 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<CharacterInfo> hires, bool createNetworkEvent = false)
@@ -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);
}
}
@@ -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);
}
@@ -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);
@@ -43,6 +43,7 @@ namespace Barotrauma
private GUIButton transferMenuButton;
private float transferMenuOpenState;
private bool transferMenuStateCompleted;
private readonly HashSet<Identifier> registeredEvents = new HashSet<Identifier>();
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<Character>();
crew = GameMain.GameSession?.CrewManager?.GetCharacters() ?? new List<Character>() { 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<Character> { 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<Character> salaryCrew = Mission.GetSalaryEligibleCrew().Where(c => c != character).ToImmutableArray();
ImmutableHashSet<Character> 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<GUILayoutGroup> 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<Character> target1 = Option<Character>.Some(character),
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.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<Character> target1 = Option<Character>.Some(character),
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.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<string, PlayerConnectionChangeType> 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<Pair<string, PlayerConnectionChangeType>> storedMessages = new List<Pair<string, PlayerConnectionChangeType>>();
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<string, PlayerConnectionChangeType>(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);
}
}
}
}
@@ -73,6 +73,8 @@ namespace Barotrauma
private Point screenResolution;
private bool needsRefresh = true;
/// <summary>
/// While set to true any call to <see cref="RefreshUpgradeList"/> 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)
@@ -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)
@@ -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<string> 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)
{
@@ -67,6 +67,8 @@ namespace Barotrauma
public void SetSoldItems(Dictionary<Identifier, List<SoldItem>> items)
{
if (SoldItems.Count == 0 && items.Count == 0) { return; }
SoldItems.Clear();
foreach (var entry in items)
{
@@ -86,30 +86,14 @@ namespace Barotrauma
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
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)));
}
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
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;
}
}
@@ -42,7 +42,7 @@ namespace Barotrauma
return PersonalWallet;
}
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
public static void StartCampaignSetup(List<SaveInfo> 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)
@@ -110,7 +110,7 @@ namespace Barotrauma
petsElement = subElement;
break;
case Wallet.LowerCaseSaveElementName:
Bank = new Wallet(subElement);
Bank = new Wallet(Option<Character>.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<Character>.None())
{
Balance = oldMoney
};
@@ -32,6 +32,7 @@ namespace Barotrauma
}
else
{
tabMenu?.OnClose();
tabMenu = null;
NetLobbyScreen.JobInfoFrame = null;
}
@@ -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<int>.Some(reward));
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
if (share > 0)
{
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
@@ -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", "")
};
@@ -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<GUILayoutGroup>();
childContainer.GetChild<GUITextBlock>().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
childContainer.GetChild<GUIImage>().Color = itemPrefab.TargetItem.InventoryIconColor * (canBeFabricated ? 1.0f : 0.5f);
childContainer.GetChild<GUIImage>().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;
}
}
}
@@ -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<Pickable>() == 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<Item> hullPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
ImmutableHashSet<Item> hullPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.Prefab.ShowInStatusMonitor && (it.GetComponent<Door>() != null || it.GetComponent<Turret>() != null)).ToImmutableHashSet();
miniMapFrame = CreateMiniMap(item.Submarine, submarineContainer, MiniMapSettings.Default, hullPointsOfInterest, out hullStatusComponents);
IEnumerable<Item> electrialPointsOfInterest = Item.ItemList.Where(it => it.Submarine == item.Submarine && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
IEnumerable<Item> electrialPointsOfInterest = Item.ItemList.Where(it => item.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true) && !it.HiddenInGame && !it.NonInteractable && it.GetComponent<Repairable>() != null);
electricalFrame = CreateMiniMap(item.Submarine, miniMapContainer, new MiniMapSettings(createHullElements: false), electrialPointsOfInterest, out electricalMapComponents);
Dictionary<MiniMapGUIComponent, GUIComponent> electricChildren = new Dictionary<MiniMapGUIComponent, GUIComponent>();
@@ -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;
@@ -271,6 +271,7 @@ namespace Barotrauma.Networking
otherClients = new List<Client>();
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<string> saveFiles = new List<string>();
byte saveCount = inc.ReadByte();
List<CampaignMode.SaveInfo> saveInfos = new List<CampaignMode.SaveInfo>();
for (int i = 0; i < saveCount; i++)
{
saveFiles.Add(inc.ReadString());
saveInfos.Add(INetSerializableStruct.Read<CampaignMode.SaveInfo>(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")
@@ -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<VoteType, int>
voteCountYes = new Dictionary<VoteType, int>(),
voteCountNo = new Dictionary<VoteType, int>(),
voteCountMax = new Dictionary<VoteType, int>();
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<Client> 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();
@@ -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<CampaignMode.SaveInfo> 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<CampaignMode.SaveInfo>();
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;
}
}
}
@@ -12,7 +12,7 @@ namespace Barotrauma
{
private GUIButton deleteMpSaveButton;
public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<string> saveFiles = null)
public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, List<CampaignMode.SaveInfo> 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<string> prevSaveFiles;
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
public void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> 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<string>();
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<string> 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;
});
@@ -21,7 +21,7 @@ namespace Barotrauma
private GUIButton nextButton;
private GUILayoutGroup characterInfoColumns;
public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<string> saveFiles = null)
public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable<SubmarineInfo> submarines, IEnumerable<CampaignMode.SaveInfo> saveFiles = null)
: base(newGameContainer, loadGameContainer)
{
UpdateNewGameMenu(submarines);
@@ -606,8 +606,7 @@ namespace Barotrauma
}
}
private List<string> prevSaveFiles;
public void UpdateLoadMenu(IEnumerable<string> saveFiles = null)
public void UpdateLoadMenu(IEnumerable<CampaignMode.SaveInfo> 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<string>();
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<GUITextBlock>().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<string> 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<GUITextBlock>().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;
});
@@ -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<GUITickBox>().Selected = Campaign.PurchasedHullRepairs;
repairItemsButton.Enabled =
(Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost)) &&
Campaign.AllowedToManageCampaign();
(Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost));
repairItemsButton.GetChild<GUITickBox>().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<GUITickBox>().Selected = Campaign.PurchasedLostShuttles;
}
break;
@@ -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))
@@ -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() { }
@@ -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))
{
@@ -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<GUITextBlock>();
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;
}
@@ -1905,27 +1905,27 @@ namespace Barotrauma
toolTip = TextManager.GetWithVariable("ServerListIncompatibleVersion", "[version]", serverInfo.GameVersion);
}
int maxIncompatibleToList = 10;
List<LocalizedString> incompatibleModNames = new List<LocalizedString>();
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;
@@ -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();
@@ -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;
@@ -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<BBWord> bbWords = new List<BBWord>();
@@ -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);
}
}
}
@@ -12,7 +12,7 @@ using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentP
namespace Barotrauma.Steam
{
public partial class WorkshopMenu
sealed partial class MutableWorkshopMenu : WorkshopMenu
{
private string ExtractTitle(ItemOrPackage itemOrPackage)
=> itemOrPackage.TryGet(out ContentPackage package)
@@ -10,7 +10,7 @@ using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentP
namespace Barotrauma.Steam
{
public partial class WorkshopMenu
sealed partial class MutableWorkshopMenu : WorkshopMenu
{
public enum Tab
{
@@ -20,10 +20,10 @@ namespace Barotrauma.Steam
Publish
}
private readonly GUILayoutGroup tabber;
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
protected readonly GUILayoutGroup tabber;
protected readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> 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<Tab, (GUIButton Button, GUIFrame Content)>();
contentFrame = new GUIFrame(new RectTransform((1.0f, 0.95f), mainLayout.RectTransform), style: null);
CreateInstalledModsTab(
out enabledCoreDropdown,
out enabledRegularModsList,
@@ -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
{
@@ -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<T>(
protected static GUIDropDown DropdownEnum<T>(
GUILayoutGroup parent, Func<T, LocalizedString> textFunc, T currentValue,
Action<T> setter) where T : Enum
=> Dropdown(parent, textFunc, (T[])Enum.GetValues(typeof(T)), currentValue, setter);
private static GUIDropDown Dropdown<T>(
protected static GUIDropDown Dropdown<T>(
GUILayoutGroup parent, Func<T, LocalizedString> textFunc, IReadOnlyList<T> values, T currentValue,
Action<T> setter, float heightScale = 1.0f)
{
@@ -67,7 +67,7 @@ namespace Barotrauma.Steam
return dropdown;
}
private static void SwapDropdownValues<T>(
protected static void SwapDropdownValues<T>(
GUIDropDown dropdown, Func<T, LocalizedString> textFunc, IReadOnlyList<T> values, T currentValue,
Action<T> 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) };
}
@@ -0,0 +1,12 @@
#nullable enable
namespace Barotrauma.Steam
{
abstract partial class WorkshopMenu
{
public WorkshopMenu(GUIFrame parent)
{
}
}
}