Unstable 0.17.5.0
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace Barotrauma
|
||||
|
||||
public void ApplyWalletData(Character character)
|
||||
{
|
||||
character.Wallet = new Wallet(WalletData);
|
||||
character.Wallet = new Wallet(Option<Character>.Some(character), WalletData);
|
||||
}
|
||||
|
||||
public XElement Save()
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Barotrauma
|
||||
DebugConsole.NewMessage("Saved campaigns:", Color.White);
|
||||
for (int i = 0; i < saveFiles.Length; i++)
|
||||
{
|
||||
DebugConsole.NewMessage(" " + i + ". " + saveFiles[i], Color.White);
|
||||
DebugConsole.NewMessage(" " + i + ". " + saveFiles[i].FilePath, Color.White);
|
||||
}
|
||||
DebugConsole.ShowQuestionPrompt("Select a save file to load (0 - " + (saveFiles.Length - 1) + "):", (string selectedSave) =>
|
||||
{
|
||||
@@ -148,7 +148,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadCampaign(saveFiles[saveIndex]);
|
||||
LoadCampaign(saveFiles[saveIndex].FilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -166,28 +166,13 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
||||
/// </summary>
|
||||
public bool AllowedToEndRound(Client client)
|
||||
{
|
||||
//allow ending the round if the client has permissions, is the owner, the only client in the server,
|
||||
//or if no-one has permissions
|
||||
return
|
||||
client.HasPermission(ClientPermissions.ManageRound) ||
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Server.ConnectedClients.Count == 1 ||
|
||||
IsOwner(client) ||
|
||||
GameMain.Server.ConnectedClients.None(c =>
|
||||
c.InGame && (IsOwner(c) || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
||||
/// </summary>
|
||||
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions = ClientPermissions.ManageCampaign)
|
||||
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
|
||||
{
|
||||
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
|
||||
//or if no-one has management permissions
|
||||
return
|
||||
client.HasPermission(permissions) ||
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Server.ConnectedClients.Count == 1 ||
|
||||
IsOwner(client) ||
|
||||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
|
||||
@@ -519,7 +504,7 @@ namespace Barotrauma
|
||||
walletsToCheck.Clear();
|
||||
walletsToCheck.Add(0, Bank);
|
||||
|
||||
foreach (Character character in Mission.GetSalaryEligibleCrew())
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Player))
|
||||
{
|
||||
walletsToCheck.Add(character.ID, character.Wallet);
|
||||
}
|
||||
@@ -715,107 +700,102 @@ namespace Barotrauma
|
||||
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
||||
}
|
||||
|
||||
bool allowedToManageCampaign = AllowedToManageCampaign(sender);
|
||||
if (AllowedToManageCampaign(sender))
|
||||
Location location = Map.CurrentLocation;
|
||||
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
|
||||
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
|
||||
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
|
||||
Wallet personalWallet = GetWallet(sender);
|
||||
|
||||
if (purchasedHullRepairs != PurchasedHullRepairs)
|
||||
{
|
||||
Location location = Map.CurrentLocation;
|
||||
int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
|
||||
int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
|
||||
int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;
|
||||
Wallet personalWallet = GetWallet(sender);
|
||||
|
||||
if (purchasedHullRepairs != PurchasedHullRepairs)
|
||||
switch (purchasedHullRepairs)
|
||||
{
|
||||
switch (purchasedHullRepairs)
|
||||
{
|
||||
case true when personalWallet.CanAfford(hullRepairCost):
|
||||
personalWallet.Deduct(hullRepairCost);
|
||||
PurchasedHullRepairs = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
||||
break;
|
||||
case false:
|
||||
PurchasedHullRepairs = false;
|
||||
personalWallet.Refund(hullRepairCost);
|
||||
break;
|
||||
}
|
||||
case true when personalWallet.CanAfford(hullRepairCost):
|
||||
personalWallet.Deduct(hullRepairCost);
|
||||
PurchasedHullRepairs = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
||||
break;
|
||||
case false:
|
||||
PurchasedHullRepairs = false;
|
||||
personalWallet.Refund(hullRepairCost);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (purchasedItemRepairs != PurchasedItemRepairs)
|
||||
if (purchasedItemRepairs != PurchasedItemRepairs)
|
||||
{
|
||||
switch (purchasedItemRepairs)
|
||||
{
|
||||
switch (purchasedItemRepairs)
|
||||
{
|
||||
case true when personalWallet.CanAfford(itemRepairCost):
|
||||
personalWallet.Deduct(itemRepairCost);
|
||||
PurchasedItemRepairs = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
||||
break;
|
||||
case false:
|
||||
PurchasedItemRepairs = false;
|
||||
personalWallet.Refund(itemRepairCost);
|
||||
break;
|
||||
}
|
||||
case true when personalWallet.CanAfford(itemRepairCost):
|
||||
personalWallet.Deduct(itemRepairCost);
|
||||
PurchasedItemRepairs = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
||||
break;
|
||||
case false:
|
||||
PurchasedItemRepairs = false;
|
||||
personalWallet.Refund(itemRepairCost);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (purchasedLostShuttles != PurchasedLostShuttles)
|
||||
if (purchasedLostShuttles != PurchasedLostShuttles)
|
||||
{
|
||||
if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
|
||||
{
|
||||
if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
|
||||
}
|
||||
else if (purchasedLostShuttles && personalWallet.TryDeduct(shuttleRetrieveCost))
|
||||
{
|
||||
PurchasedLostShuttles = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
||||
}
|
||||
else if (!purchasedItemRepairs)
|
||||
{
|
||||
PurchasedLostShuttles = false;
|
||||
personalWallet.Refund(shuttleRetrieveCost);
|
||||
}
|
||||
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
|
||||
}
|
||||
|
||||
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
|
||||
else if (purchasedLostShuttles && personalWallet.TryDeduct(shuttleRetrieveCost))
|
||||
{
|
||||
Map.SetLocation(currentLocIndex);
|
||||
PurchasedLostShuttles = true;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
||||
}
|
||||
else if (!purchasedItemRepairs)
|
||||
{
|
||||
PurchasedLostShuttles = false;
|
||||
personalWallet.Refund(shuttleRetrieveCost);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
|
||||
{
|
||||
Map.SetLocation(currentLocIndex);
|
||||
}
|
||||
|
||||
if (AllowedToManageCampaign(sender, ClientPermissions.ManageMap))
|
||||
{
|
||||
Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); }
|
||||
if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); }
|
||||
CheckTooManyMissions(Map.CurrentLocation, sender);
|
||||
}
|
||||
|
||||
bool allowedToUseStore = AllowedToManageCampaign(sender, ClientPermissions.CampaignStore);
|
||||
if (allowedToManageCampaign || allowedToUseStore || AllowedToManageCampaign(sender, ClientPermissions.BuyItems))
|
||||
|
||||
var prevBuyCrateItems = new Dictionary<Identifier, List<PurchasedItem>>(CargoManager.ItemsInBuyCrate);
|
||||
foreach (var store in prevBuyCrateItems)
|
||||
{
|
||||
var prevBuyCrateItems = new Dictionary<Identifier, List<PurchasedItem>>(CargoManager.ItemsInBuyCrate);
|
||||
foreach (var store in prevBuyCrateItems)
|
||||
foreach (var item in store.Value)
|
||||
{
|
||||
foreach (var item in store.Value)
|
||||
{
|
||||
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, -item.Quantity, sender);
|
||||
}
|
||||
}
|
||||
foreach (var store in buyCrateItems)
|
||||
{
|
||||
foreach (var item in store.Value)
|
||||
{
|
||||
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
|
||||
}
|
||||
}
|
||||
var prevPurchasedItems = new Dictionary<Identifier, List<PurchasedItem>>(CargoManager.PurchasedItems);
|
||||
foreach (var store in prevPurchasedItems)
|
||||
{
|
||||
CargoManager.SellBackPurchasedItems(store.Key, store.Value);
|
||||
}
|
||||
foreach (var store in purchasedItems)
|
||||
{
|
||||
CargoManager.PurchaseItems(store.Key, store.Value, false, sender);
|
||||
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, -item.Quantity, sender);
|
||||
}
|
||||
}
|
||||
foreach (var store in buyCrateItems)
|
||||
{
|
||||
foreach (var item in store.Value)
|
||||
{
|
||||
CargoManager.ModifyItemQuantityInBuyCrate(store.Key, item.ItemPrefab, item.Quantity, sender);
|
||||
}
|
||||
}
|
||||
var prevPurchasedItems = new Dictionary<Identifier, List<PurchasedItem>>(CargoManager.PurchasedItems);
|
||||
foreach (var store in prevPurchasedItems)
|
||||
{
|
||||
CargoManager.SellBackPurchasedItems(store.Key, store.Value);
|
||||
}
|
||||
foreach (var store in purchasedItems)
|
||||
{
|
||||
CargoManager.PurchaseItems(store.Key, store.Value, false, sender);
|
||||
}
|
||||
|
||||
bool allowedToSellSubItems = AllowedToManageCampaign(sender, ClientPermissions.SellSubItems);
|
||||
if (allowedToManageCampaign || allowedToUseStore || allowedToSellSubItems)
|
||||
if (allowedToSellSubItems)
|
||||
{
|
||||
var prevSubSellCrateItems = new Dictionary<Identifier, List<PurchasedItem>>(CargoManager.ItemsInSellFromSubCrate);
|
||||
foreach (var store in prevSubSellCrateItems)
|
||||
@@ -834,7 +814,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
bool allowedToSellInventoryItems = AllowedToManageCampaign(sender, ClientPermissions.SellInventoryItems);
|
||||
if (allowedToManageCampaign || allowedToUseStore || (allowedToSellInventoryItems && allowedToSellSubItems))
|
||||
if (allowedToSellInventoryItems && allowedToSellSubItems)
|
||||
{
|
||||
// for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all
|
||||
// sold items that are removed so they should be discarded on the next message
|
||||
@@ -867,37 +847,34 @@ namespace Barotrauma
|
||||
bool predicate(SoldItem i) => allowedToSellInventoryItems != (i.Origin == SoldItem.SellOrigin.Character);
|
||||
}
|
||||
|
||||
if (allowedToManageCampaign)
|
||||
foreach (var (prefab, category, _) in purchasedUpgrades)
|
||||
{
|
||||
foreach (var (prefab, category, _) in purchasedUpgrades)
|
||||
{
|
||||
UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
|
||||
UpgradeManager.PurchaseUpgrade(prefab, category, client: sender);
|
||||
|
||||
// unstable logging
|
||||
int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
|
||||
int level = UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||
GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
||||
// unstable logging
|
||||
int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
|
||||
int level = UpgradeManager.GetUpgradeLevel(prefab, category);
|
||||
GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
||||
{
|
||||
if (purchasedItemSwap.ItemToInstall == null)
|
||||
{
|
||||
if (purchasedItemSwap.ItemToInstall == null)
|
||||
{
|
||||
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, client: sender);
|
||||
}
|
||||
UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
else
|
||||
{
|
||||
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
|
||||
{
|
||||
UpgradeManager.CancelItemSwap(item);
|
||||
item.PendingItemSwap = null;
|
||||
}
|
||||
UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, client: sender);
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
|
||||
{
|
||||
UpgradeManager.CancelItemSwap(item);
|
||||
item.PendingItemSwap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerReadMoney(IReadMessage msg, Client sender)
|
||||
@@ -907,19 +884,26 @@ namespace Barotrauma
|
||||
switch (transfer.Sender)
|
||||
{
|
||||
case Some<ushort> { Value: var id }:
|
||||
|
||||
if (id != sender.CharacterID && !AllowedToManageCampaign(sender)) { return; }
|
||||
if (id != sender.CharacterID && !AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; }
|
||||
|
||||
Wallet wallet = GetWalletByID(id);
|
||||
if (wallet is InvalidWallet) { return; }
|
||||
|
||||
TransferMoney(wallet);
|
||||
break;
|
||||
|
||||
case None<ushort> _:
|
||||
if (!AllowedToManageCampaign(sender)) { return; }
|
||||
|
||||
TransferMoney(Bank);
|
||||
if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney))
|
||||
{
|
||||
if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID)
|
||||
{
|
||||
GameMain.Server?.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransferMoney(Bank);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -952,10 +936,10 @@ namespace Barotrauma
|
||||
{
|
||||
NetWalletSetSalaryUpdate update = INetSerializableStruct.Read<NetWalletSetSalaryUpdate>(msg);
|
||||
|
||||
if (!AllowedToManageCampaign(sender)) { return; }
|
||||
if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; }
|
||||
|
||||
Character targetCharacter = Character.CharacterList.FirstOrDefault(c => c.ID == update.Target);
|
||||
targetCharacter?.Wallet.SetRewardDistrubiton(update.NewRewardDistribution);
|
||||
targetCharacter?.Wallet.SetRewardDistribution(update.NewRewardDistribution);
|
||||
}
|
||||
|
||||
public void ServerReadCrew(IReadMessage msg, Client sender)
|
||||
@@ -994,7 +978,7 @@ namespace Barotrauma
|
||||
List<CharacterInfo> hiredCharacters = new List<CharacterInfo>();
|
||||
CharacterInfo firedCharacter = null;
|
||||
|
||||
if (location != null && AllowedToManageCampaign(sender))
|
||||
if (location != null && AllowedToManageCampaign(sender, ClientPermissions.ManageHires))
|
||||
{
|
||||
if (fireCharacter)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
private struct RateLimitInfo
|
||||
{
|
||||
public int Requests;
|
||||
public const int MaxRequests = 5;
|
||||
public const int MaxRequests = 10;
|
||||
public DateTimeOffset Expiry;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,8 @@ namespace Barotrauma.Networking
|
||||
KarmaManager.SelectPreset(serverSettings.KarmaPreset);
|
||||
serverSettings.SetPassword(password);
|
||||
|
||||
Voting = new Voting();
|
||||
|
||||
ownerKey = ownKey;
|
||||
|
||||
ownerSteamId = steamId;
|
||||
@@ -383,23 +385,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
TraitorManager?.Update(deltaTime);
|
||||
|
||||
if (serverSettings.Voting.VoteRunning)
|
||||
{
|
||||
Voting.SubVote.Timer += deltaTime;
|
||||
|
||||
if (Voting.SubVote.Timer >= serverSettings.SubmarineVoteTimeout)
|
||||
{
|
||||
// Do not take unanswered into account for total
|
||||
if (SubmarineVoteYesCount / (float)(SubmarineVoteYesCount + SubmarineVoteNoCount) >= serverSettings.SubmarineVoteRequiredRatio)
|
||||
{
|
||||
SwitchSubmarine();
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSettings.Voting.StopSubmarineVote(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Voting.Update(deltaTime);
|
||||
|
||||
bool isCrewDead =
|
||||
connectedClients.All(c => c.Character == null || c.Character.IsDead || c.Character.IsIncapacitated);
|
||||
@@ -1073,7 +1059,7 @@ namespace Barotrauma.Networking
|
||||
ChatMessage.ServerRead(inc, c);
|
||||
break;
|
||||
case ClientNetObject.VOTE:
|
||||
serverSettings.Voting.ServerRead(inc, c);
|
||||
Voting.ServerRead(inc, c);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -1220,7 +1206,7 @@ namespace Barotrauma.Networking
|
||||
entityEventManager.Read(inc, c);
|
||||
break;
|
||||
case ClientNetObject.VOTE:
|
||||
serverSettings.Voting.ServerRead(inc, c);
|
||||
Voting.ServerRead(inc, c);
|
||||
break;
|
||||
case ClientNetObject.SPECTATING_POS:
|
||||
c.SpectatePos = new Vector2(inc.ReadSingle(), inc.ReadSingle());
|
||||
@@ -1309,17 +1295,11 @@ namespace Barotrauma.Networking
|
||||
var mpCampaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
||||
if (command == ClientPermissions.ManageRound && mpCampaign != null)
|
||||
{
|
||||
if (!mpCampaign.AllowedToEndRound(sender))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//do nothing, ending campaign rounds is checked in more detail below
|
||||
}
|
||||
else if (command == ClientPermissions.ManageCampaign && mpCampaign != null)
|
||||
{
|
||||
if (!mpCampaign.AllowedToManageCampaign(sender))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//do nothing, campaign permissions are checked in more detail in MultiplayerCampaign.ServerRead
|
||||
}
|
||||
else if (!sender.HasPermission(command))
|
||||
{
|
||||
@@ -1381,21 +1361,26 @@ namespace Barotrauma.Networking
|
||||
bool end = inc.ReadBoolean();
|
||||
if (end)
|
||||
{
|
||||
bool save = inc.ReadBoolean();
|
||||
if (gameStarted)
|
||||
if (mpCampaign == null ||
|
||||
mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageRound) ||
|
||||
mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign))
|
||||
{
|
||||
Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
|
||||
if (mpCampaign != null && Level.IsLoadedOutpost && save)
|
||||
bool save = inc.ReadBoolean();
|
||||
if (gameStarted)
|
||||
{
|
||||
mpCampaign.SavePlayers();
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
|
||||
if (mpCampaign != null && Level.IsLoadedOutpost && save)
|
||||
{
|
||||
mpCampaign.SavePlayers();
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
save = false;
|
||||
}
|
||||
EndGame(wasSaved: save);
|
||||
}
|
||||
else
|
||||
{
|
||||
save = false;
|
||||
}
|
||||
EndGame(wasSaved: save);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1403,14 +1388,17 @@ namespace Barotrauma.Networking
|
||||
bool continueCampaign = inc.ReadBoolean();
|
||||
if (mpCampaign != null && mpCampaign.GameOver || continueCampaign)
|
||||
{
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
||||
if (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap))
|
||||
{
|
||||
MultiPlayerCampaign.LoadCampaign(GameMain.GameSession.SavePath);
|
||||
}
|
||||
}
|
||||
else if (!gameStarted && !initiatedStartGame)
|
||||
{
|
||||
Log("Client \"" + ClientLogName(sender) + "\" started the round.", ServerLog.MessageType.ServerMessage);
|
||||
StartGame();
|
||||
}
|
||||
else if (mpCampaign != null)
|
||||
else if (mpCampaign != null && (mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageCampaign) || mpCampaign.AllowedToManageCampaign(sender, ClientPermissions.ManageMap)))
|
||||
{
|
||||
var availableTransition = mpCampaign.GetAvailableTransition(out _, out _);
|
||||
//don't force location if we've teleported
|
||||
@@ -1473,34 +1461,20 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (GameMain.NetLobbyScreen.GameModes[modeIndex].Identifier == "multiplayercampaign")
|
||||
{
|
||||
string[] saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false).ToArray();
|
||||
for (int i = 0; i < saveFiles.Length; i++)
|
||||
{
|
||||
XDocument doc = SaveUtil.LoadGameSessionDoc(saveFiles[i]);
|
||||
if (doc?.Root != null)
|
||||
{
|
||||
saveFiles[i] =
|
||||
string.Join(";",
|
||||
saveFiles[i].Replace(';', ' '),
|
||||
doc.Root.GetAttributeStringUnrestricted("submarine", ""),
|
||||
doc.Root.GetAttributeStringUnrestricted("savetime", ""),
|
||||
doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", ""));
|
||||
}
|
||||
}
|
||||
|
||||
const int MaxSaves = 255;
|
||||
var saveInfos = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer, includeInCompatible: false);
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ServerPacketHeader.CAMPAIGN_SETUP_INFO);
|
||||
msg.Write((UInt16)saveFiles.Count());
|
||||
foreach (string saveFile in saveFiles)
|
||||
msg.Write((byte)Math.Min(saveInfos.Count, MaxSaves));
|
||||
for (int i = 0; i < saveInfos.Count && i < MaxSaves; i++)
|
||||
{
|
||||
msg.Write(saveFile);
|
||||
msg.Write(saveInfos[i]);
|
||||
}
|
||||
|
||||
serverPeer.Send(msg, sender.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
break;
|
||||
case ClientPermissions.ManageCampaign:
|
||||
(GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender);
|
||||
mpCampaign?.ServerRead(inc, sender);
|
||||
break;
|
||||
case ClientPermissions.ConsoleCommands:
|
||||
{
|
||||
@@ -1900,8 +1874,8 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write(selectedShuttle.Name);
|
||||
outmsg.Write(selectedShuttle.MD5Hash.ToString());
|
||||
|
||||
outmsg.Write(serverSettings.Voting.AllowSubVoting);
|
||||
outmsg.Write(serverSettings.Voting.AllowModeVoting);
|
||||
outmsg.Write(serverSettings.AllowSubVoting);
|
||||
outmsg.Write(serverSettings.AllowModeVoting);
|
||||
|
||||
outmsg.Write(serverSettings.VoiceChatEnabled);
|
||||
|
||||
@@ -2036,9 +2010,9 @@ namespace Barotrauma.Networking
|
||||
SubmarineInfo selectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle;
|
||||
|
||||
SubmarineInfo selectedSub;
|
||||
if (serverSettings.Voting.AllowSubVoting)
|
||||
if (serverSettings.AllowSubVoting)
|
||||
{
|
||||
selectedSub = serverSettings.Voting.HighestVoted<SubmarineInfo>(VoteType.Sub, connectedClients);
|
||||
selectedSub = Voting.HighestVoted<SubmarineInfo>(VoteType.Sub, connectedClients);
|
||||
if (selectedSub == null) { selectedSub = GameMain.NetLobbyScreen.SelectedSub; }
|
||||
}
|
||||
else
|
||||
@@ -2051,7 +2025,7 @@ namespace Barotrauma.Networking
|
||||
return false;
|
||||
}
|
||||
|
||||
GameModePreset selectedMode = serverSettings.Voting.HighestVoted<GameModePreset>(VoteType.Mode, connectedClients);
|
||||
GameModePreset selectedMode = Voting.HighestVoted<GameModePreset>(VoteType.Mode, connectedClients);
|
||||
if (selectedMode == null) { selectedMode = GameMain.NetLobbyScreen.SelectedMode; }
|
||||
|
||||
if (selectedMode == null)
|
||||
@@ -2455,11 +2429,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (GameMain.Server?.ServerSettings?.Voting != null)
|
||||
{
|
||||
GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
|
||||
}
|
||||
|
||||
Voting?.ResetVotes(GameMain.Server.ConnectedClients);
|
||||
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
Log("Round started.", ServerLog.MessageType.ServerMessage);
|
||||
@@ -3233,23 +3204,24 @@ namespace Barotrauma.Networking
|
||||
serverPeer.Send(msg, transfer.Connection, DeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
public void UpdateVoteStatus()
|
||||
public void UpdateVoteStatus(bool checkActiveVote = true)
|
||||
{
|
||||
if (connectedClients.Count == 0) return;
|
||||
if (connectedClients.Count == 0) { return; }
|
||||
|
||||
if (serverSettings.Voting.VoteRunning)
|
||||
if (checkActiveVote && Voting.ActiveVote != null)
|
||||
{
|
||||
int yes = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote<int>(Voting.ActiveVote.VoteType) == 2);
|
||||
int no = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote<int>(Voting.ActiveVote.VoteType) == 1);
|
||||
int max = GameMain.Server.ConnectedClients.Count(c => c.InGame);
|
||||
// Required ratio cannot be met
|
||||
if (SubmarineVoteNoCount / (float)SubmarineVoteMax > 1f - serverSettings.SubmarineVoteRequiredRatio)
|
||||
if (no / (float)max > 1f - serverSettings.VoteRequiredRatio)
|
||||
{
|
||||
serverSettings.Voting.StopSubmarineVote(false);
|
||||
return; // Update will be re-sent via StopSubmarineVote
|
||||
Voting.ActiveVote.Finish(Voting, passed: false);
|
||||
}
|
||||
else if (SubmarineVoteYesCount / (float)SubmarineVoteMax >= serverSettings.SubmarineVoteRequiredRatio)
|
||||
else if (yes / max >= serverSettings.VoteRequiredRatio)
|
||||
{
|
||||
SwitchSubmarine();
|
||||
return; // Update will be re-sent via StopSubmarineVote
|
||||
}
|
||||
Voting.ActiveVote.Finish(Voting, passed: true);
|
||||
}
|
||||
}
|
||||
|
||||
Client.UpdateKickVotes(connectedClients);
|
||||
@@ -3279,10 +3251,12 @@ namespace Barotrauma.Networking
|
||||
|
||||
SendVoteStatus(connectedClients);
|
||||
|
||||
if (serverSettings.Voting.AllowEndVoting && EndVoteMax > 0 &&
|
||||
((float)EndVoteCount / (float)EndVoteMax) >= serverSettings.EndVoteRequiredRatio)
|
||||
int endVoteCount = ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));
|
||||
int endVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
|
||||
if (serverSettings.AllowEndVoting && endVoteMax > 0 &&
|
||||
((float)endVoteCount / (float)endVoteMax) >= serverSettings.EndVoteRequiredRatio)
|
||||
{
|
||||
Log("Ending round by votes (" + EndVoteCount + "/" + (EndVoteMax - EndVoteCount) + ")", ServerLog.MessageType.ServerMessage);
|
||||
Log("Ending round by votes (" + endVoteCount + "/" + (endVoteMax - endVoteCount) + ")", ServerLog.MessageType.ServerMessage);
|
||||
EndGame(wasSaved: false);
|
||||
}
|
||||
}
|
||||
@@ -3294,7 +3268,7 @@ namespace Barotrauma.Networking
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY);
|
||||
msg.Write((byte)ServerNetObject.VOTE);
|
||||
serverSettings.Voting.ServerWrite(msg);
|
||||
Voting.ServerWrite(msg);
|
||||
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
|
||||
|
||||
foreach (var c in recipients)
|
||||
@@ -3303,11 +3277,13 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchSubmarine()
|
||||
public void SwitchSubmarine()
|
||||
{
|
||||
SubmarineInfo targetSubmarine = Voting.SubVote.Sub;
|
||||
VoteType voteType = Voting.SubVote.VoteType;
|
||||
Client starter = Voting.SubVote.VoteStarter;
|
||||
if (!(Voting.ActiveVote is Voting.SubmarineVote subVote)) { return; }
|
||||
|
||||
SubmarineInfo targetSubmarine = subVote.Sub;
|
||||
VoteType voteType = Voting.ActiveVote.VoteType;
|
||||
Client starter = Voting.ActiveVote.VoteStarter;
|
||||
int deliveryFee = 0;
|
||||
|
||||
switch (voteType)
|
||||
@@ -3318,7 +3294,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.GameSession.PurchaseSubmarine(targetSubmarine, starter);
|
||||
break;
|
||||
case VoteType.SwitchSub:
|
||||
deliveryFee = Voting.SubVote.DeliveryFee;
|
||||
deliveryFee = subVote.DeliveryFee;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -3326,10 +3302,10 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (voteType != VoteType.PurchaseSub)
|
||||
{
|
||||
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee, starter);
|
||||
GameMain.GameSession.SwitchSubmarine(targetSubmarine, deliveryFee, starter);
|
||||
}
|
||||
|
||||
serverSettings.Voting.StopSubmarineVote(true);
|
||||
Voting.StopSubmarineVote(true);
|
||||
}
|
||||
|
||||
public void UpdateClientPermissions(Client client)
|
||||
|
||||
@@ -312,8 +312,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
AutoRestart = doc.Root.GetAttributeBool("autorestart", false);
|
||||
|
||||
Voting.AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
|
||||
Voting.AllowModeVoting = ModeSelectionMode == SelectionMode.Vote;
|
||||
AllowSubVoting = SubSelectionMode == SelectionMode.Vote;
|
||||
AllowModeVoting = ModeSelectionMode == SelectionMode.Vote;
|
||||
|
||||
selectedLevelDifficulty = doc.Root.GetAttributeFloat("LevelDifficulty", 20.0f);
|
||||
GameMain.NetLobbyScreen.SetLevelDifficulty(selectedLevelDifficulty);
|
||||
|
||||
@@ -7,59 +7,161 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Voting
|
||||
{
|
||||
public bool AllowSubVoting
|
||||
public interface IVote
|
||||
{
|
||||
get { return allowSubVoting; }
|
||||
set { allowSubVoting = value; }
|
||||
}
|
||||
public bool AllowModeVoting
|
||||
{
|
||||
get { return allowModeVoting; }
|
||||
set { allowModeVoting = value; }
|
||||
}
|
||||
public Client VoteStarter { get; }
|
||||
public VoteType VoteType { get; }
|
||||
public float Timer { get; set; }
|
||||
|
||||
public struct SubmarineVote
|
||||
public VoteState State { get; set; }
|
||||
|
||||
public void Finish(Voting voting, bool passed);
|
||||
}
|
||||
|
||||
public class SubmarineVote : IVote
|
||||
{
|
||||
public Client VoteStarter;
|
||||
public Client VoteStarter { get; }
|
||||
public VoteType VoteType { get; }
|
||||
public float Timer { get; set; }
|
||||
|
||||
public VoteState State { get; set; }
|
||||
|
||||
public SubmarineInfo Sub;
|
||||
public VoteType VoteType;
|
||||
public float Timer;
|
||||
public int DeliveryFee;
|
||||
public VoteState State;
|
||||
|
||||
public SubmarineVote(Client starter, SubmarineInfo subInfo, int deliveryFee, VoteType voteType)
|
||||
{
|
||||
Sub = subInfo;
|
||||
DeliveryFee = deliveryFee;
|
||||
VoteType = voteType;
|
||||
State = VoteState.Started;
|
||||
VoteStarter = starter;
|
||||
}
|
||||
|
||||
public void Finish(Voting voting, bool passed)
|
||||
{
|
||||
if (passed)
|
||||
{
|
||||
GameMain.Server?.SwitchSubmarine();
|
||||
}
|
||||
voting.StopSubmarineVote(passed);
|
||||
}
|
||||
}
|
||||
|
||||
public static SubmarineVote SubVote;
|
||||
public static IVote ActiveVote;
|
||||
|
||||
public class TransferVote : IVote
|
||||
{
|
||||
public Client VoteStarter { get; }
|
||||
public VoteType VoteType { get; }
|
||||
public float Timer { get; set; }
|
||||
|
||||
public VoteState State { get; set; }
|
||||
|
||||
//null = bank
|
||||
public readonly Client From, To;
|
||||
public readonly int TransferAmount;
|
||||
|
||||
public TransferVote(Client starter, Client from, int transferAmount, Client to)
|
||||
{
|
||||
VoteStarter = starter;
|
||||
From = from;
|
||||
To = to;
|
||||
TransferAmount = transferAmount;
|
||||
State = VoteState.Started;
|
||||
VoteType = VoteType.TransferMoney;
|
||||
}
|
||||
|
||||
public void Finish(Voting voting, bool passed)
|
||||
{
|
||||
if (passed)
|
||||
{
|
||||
Wallet fromWallet = From == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : From.Character?.Wallet;
|
||||
if (fromWallet.TryDeduct(TransferAmount))
|
||||
{
|
||||
Wallet toWallet = To == null ? (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.Bank : To.Character?.Wallet;
|
||||
toWallet.Give(TransferAmount);
|
||||
}
|
||||
}
|
||||
voting.StopMoneyTransferVote(passed);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Queue<IVote> pendingVotes = new Queue<IVote>();
|
||||
|
||||
private void StartSubmarineVote(SubmarineInfo subInfo, VoteType voteType, Client sender)
|
||||
{
|
||||
SubVote.Sub = subInfo;
|
||||
SubVote.DeliveryFee = voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0;
|
||||
SubVote.VoteType = voteType;
|
||||
SubVote.State = VoteState.Started;
|
||||
SubVote.VoteStarter = sender;
|
||||
VoteRunning = true;
|
||||
var subVote = new SubmarineVote(
|
||||
sender,
|
||||
subInfo,
|
||||
voteType == VoteType.SwitchSub ? GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation) : 0,
|
||||
voteType);
|
||||
StartOrEnqueueVote(subVote);
|
||||
sender.SetVote(voteType, 2);
|
||||
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
|
||||
}
|
||||
|
||||
public void StopSubmarineVote(bool passed)
|
||||
{
|
||||
VoteRunning = false;
|
||||
SubVote.State = passed ? VoteState.Passed : VoteState.Failed;
|
||||
if (!(ActiveVote is SubmarineVote)) { return; }
|
||||
StopActiveVote(passed);
|
||||
}
|
||||
|
||||
GameMain.Server.UpdateVoteStatus();
|
||||
public void StopMoneyTransferVote(bool passed)
|
||||
{
|
||||
if (!(ActiveVote is TransferVote)) { return; }
|
||||
StopActiveVote(passed);
|
||||
}
|
||||
|
||||
public void StopActiveVote(bool passed)
|
||||
{
|
||||
ActiveVote.State = passed ? VoteState.Passed : VoteState.Failed;
|
||||
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
|
||||
|
||||
GameMain.NetworkMember.SubmarineVoteYesCount = GameMain.NetworkMember.SubmarineVoteNoCount = GameMain.NetworkMember.SubmarineVoteMax = 0;
|
||||
for (int i = 0; i < GameMain.NetworkMember.ConnectedClients.Count; i++)
|
||||
{
|
||||
GameMain.NetworkMember.ConnectedClients[i].SetVote(SubVote.VoteType, 0);
|
||||
GameMain.NetworkMember.ConnectedClients[i].SetVote(ActiveVote.VoteType, 0);
|
||||
}
|
||||
|
||||
SubVote.Sub = null;
|
||||
SubVote.DeliveryFee = 0;
|
||||
SubVote.VoteType = VoteType.Unknown;
|
||||
SubVote.Timer = 0.0f;
|
||||
SubVote.State = VoteState.None;
|
||||
SubVote.VoteStarter = null;
|
||||
ActiveVote = null;
|
||||
if (pendingVotes.Any())
|
||||
{
|
||||
ActiveVote = pendingVotes.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void StartTransferVote(Client starter, Client from, int transferAmount, Client to)
|
||||
{
|
||||
StartOrEnqueueVote(new TransferVote(starter, from, transferAmount, to));
|
||||
starter.SetVote(VoteType.TransferMoney, 2);
|
||||
GameMain.Server.UpdateVoteStatus(checkActiveVote: false);
|
||||
}
|
||||
|
||||
private void StartOrEnqueueVote(IVote vote)
|
||||
{
|
||||
if (ActiveVote == null)
|
||||
{
|
||||
ActiveVote = vote;
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingVotes.Enqueue(vote);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ActiveVote == null) { return; }
|
||||
|
||||
ActiveVote.Timer += deltaTime;
|
||||
|
||||
if (ActiveVote.Timer >= GameMain.NetworkMember.ServerSettings.VoteTimeout)
|
||||
{
|
||||
// Do not take unanswered into account for total
|
||||
int yes = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote<int>(ActiveVote.VoteType) == 2);
|
||||
int no = GameMain.Server.ConnectedClients.Count(c => c.InGame && c.GetVote<int>(ActiveVote.VoteType) == 1);
|
||||
ActiveVote.Finish(this, passed: yes / (float)(yes + no) >= GameMain.NetworkMember.ServerSettings.VoteRequiredRatio);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(IReadMessage inc, Client sender)
|
||||
@@ -97,10 +199,6 @@ namespace Barotrauma
|
||||
case VoteType.EndRound:
|
||||
if (!sender.HasSpawned) { return; }
|
||||
sender.SetVote(voteType, inc.ReadBoolean());
|
||||
|
||||
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));
|
||||
GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
|
||||
|
||||
break;
|
||||
case VoteType.Kick:
|
||||
byte kickedClientID = inc.ReadByte();
|
||||
@@ -126,24 +224,34 @@ namespace Barotrauma
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.PurchaseSub:
|
||||
case VoteType.SwitchSub:
|
||||
case VoteType.TransferMoney:
|
||||
bool startVote = inc.ReadBoolean();
|
||||
if (startVote)
|
||||
{
|
||||
string subName = inc.ReadString();
|
||||
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
|
||||
if (voteType == VoteType.TransferMoney)
|
||||
{
|
||||
StartSubmarineVote(subInfo, voteType, sender);
|
||||
int amount = inc.ReadInt32();
|
||||
int fromClientId = inc.ReadByte();
|
||||
int toClientId = inc.ReadByte();
|
||||
pendingVotes.Enqueue(new TransferVote(sender,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
|
||||
amount,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
|
||||
}
|
||||
else
|
||||
{
|
||||
string subName = inc.ReadString();
|
||||
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
|
||||
{
|
||||
StartSubmarineVote(subInfo, voteType, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sender.SetVote(voteType, (int)inc.ReadByte());
|
||||
}
|
||||
|
||||
GameMain.Server.SubmarineVoteYesCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote<int>(SubVote.VoteType) == 2);
|
||||
GameMain.Server.SubmarineVoteNoCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote<int>(SubVote.VoteType) == 1);
|
||||
GameMain.Server.SubmarineVoteMax = GameMain.Server.ConnectedClients.Count(c => c.InGame);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -154,10 +262,10 @@ namespace Barotrauma
|
||||
|
||||
public void ServerWrite(IWriteMessage msg)
|
||||
{
|
||||
if (GameMain.Server == null) return;
|
||||
if (GameMain.Server == null) { return; }
|
||||
|
||||
msg.Write(allowSubVoting);
|
||||
if (allowSubVoting)
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowSubVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowSubVoting)
|
||||
{
|
||||
IReadOnlyDictionary<SubmarineInfo, int> voteList = GetVoteCounts<SubmarineInfo>(VoteType.Sub, GameMain.Server.ConnectedClients);
|
||||
msg.Write((byte)voteList.Count);
|
||||
@@ -167,8 +275,8 @@ namespace Barotrauma
|
||||
msg.Write(vote.Key.Name);
|
||||
}
|
||||
}
|
||||
msg.Write(AllowModeVoting);
|
||||
if (allowModeVoting)
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowModeVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowModeVoting)
|
||||
{
|
||||
IReadOnlyDictionary<GameModePreset, int> voteList = GetVoteCounts<GameModePreset>(VoteType.Mode, GameMain.Server.ConnectedClients);
|
||||
msg.Write((byte)voteList.Count);
|
||||
@@ -178,60 +286,78 @@ namespace Barotrauma
|
||||
msg.Write(vote.Key.Identifier);
|
||||
}
|
||||
}
|
||||
msg.Write(AllowEndVoting);
|
||||
if (AllowEndVoting)
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowEndVoting);
|
||||
if (GameMain.Server.ServerSettings.AllowEndVoting)
|
||||
{
|
||||
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound)));
|
||||
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.HasSpawned));
|
||||
}
|
||||
|
||||
msg.Write(AllowVoteKick);
|
||||
msg.Write(GameMain.Server.ServerSettings.AllowVoteKick);
|
||||
|
||||
msg.Write((byte)SubVote.State);
|
||||
if (SubVote.State != VoteState.None)
|
||||
msg.Write((byte)(ActiveVote?.State ?? VoteState.None));
|
||||
if (ActiveVote != null)
|
||||
{
|
||||
msg.Write((byte)SubVote.VoteType);
|
||||
|
||||
if (SubVote.VoteType != VoteType.Unknown)
|
||||
{
|
||||
var yesClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<int>(SubVote.VoteType) == 2);
|
||||
msg.Write((byte)ActiveVote.VoteType);
|
||||
if (ActiveVote.State != VoteState.None && ActiveVote.VoteType != VoteType.Unknown)
|
||||
{
|
||||
var yesClients = GameMain.Server.ConnectedClients.FindAll(c => c.InGame && c.GetVote<int>(ActiveVote.VoteType) == 2);
|
||||
msg.Write((byte)yesClients.Count);
|
||||
foreach (Client c in yesClients)
|
||||
{
|
||||
msg.Write(c.ID);
|
||||
}
|
||||
|
||||
var noClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<int>(SubVote.VoteType) == 1);
|
||||
var noClients = GameMain.Server.ConnectedClients.FindAll(c => c.InGame && c.GetVote<int>(ActiveVote.VoteType) == 1);
|
||||
msg.Write((byte)noClients.Count);
|
||||
foreach (Client c in noClients)
|
||||
{
|
||||
msg.Write(c.ID);
|
||||
}
|
||||
|
||||
msg.Write((byte)GameMain.Server.SubmarineVoteMax);
|
||||
msg.Write((byte)GameMain.Server.ConnectedClients.Count(c => c.InGame));
|
||||
|
||||
switch (SubVote.State)
|
||||
switch (ActiveVote.State)
|
||||
{
|
||||
case VoteState.Started:
|
||||
msg.Write(SubVote.Sub.Name);
|
||||
msg.Write(SubVote.VoteStarter.ID);
|
||||
msg.Write((byte)GameMain.Server.ServerSettings.SubmarineVoteTimeout);
|
||||
msg.Write(ActiveVote.VoteStarter.ID);
|
||||
msg.Write((byte)GameMain.Server.ServerSettings.VoteTimeout);
|
||||
|
||||
switch (ActiveVote.VoteType)
|
||||
{
|
||||
case VoteType.PurchaseSub:
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.SwitchSub:
|
||||
msg.Write((ActiveVote as SubmarineVote).Sub.Name);
|
||||
break;
|
||||
case VoteType.TransferMoney:
|
||||
var transferVote = (ActiveVote as TransferVote);
|
||||
msg.Write(transferVote.From?.ID ?? 0);
|
||||
msg.Write(transferVote.To?.ID ?? 0);
|
||||
msg.Write(transferVote.TransferAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case VoteState.Running:
|
||||
// Nothing specific
|
||||
break;
|
||||
case VoteState.Passed:
|
||||
case VoteState.Failed:
|
||||
msg.Write(SubVote.State == VoteState.Passed);
|
||||
msg.Write(SubVote.Sub.Name);
|
||||
if (SubVote.State == VoteState.Passed)
|
||||
msg.Write(ActiveVote.State == VoteState.Passed);
|
||||
switch (ActiveVote.VoteType)
|
||||
{
|
||||
msg.Write((short)SubVote.DeliveryFee);
|
||||
case VoteType.PurchaseSub:
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.SwitchSub:
|
||||
msg.Write((ActiveVote as SubmarineVote).Sub.Name);
|
||||
msg.Write((short)(ActiveVote as SubmarineVote).DeliveryFee);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var readyClients = GameMain.Server.ConnectedClients.FindAll(c => c.GetVote<bool>(VoteType.StartRound));
|
||||
msg.Write((byte)readyClients.Count);
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Barotrauma
|
||||
public override void Select()
|
||||
{
|
||||
base.Select();
|
||||
GameMain.Server.ServerSettings.Voting.ResetVotes(GameMain.Server.ConnectedClients);
|
||||
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients);
|
||||
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
|
||||
{
|
||||
GameMain.GameSession = null;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>0.17.5.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<Preset
|
||||
name="Moderator"
|
||||
description="Allowed to manage round settings, kick players and access server logs."
|
||||
permissions="ManageRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog">
|
||||
permissions="ManageRound,Kick,SelectSub,SelectMode,ManageCampaign,ConsoleCommands,ServerLog,ManageSettings,ManageMoney">
|
||||
<Command name="clientlist"/>
|
||||
<Command name="readycheck"/>
|
||||
<Command name="autorestart"/>
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Wallet wallet = new Wallet();
|
||||
private Wallet wallet;
|
||||
|
||||
public Wallet Wallet
|
||||
{
|
||||
@@ -1055,8 +1055,13 @@ namespace Barotrauma
|
||||
return newCharacter;
|
||||
}
|
||||
|
||||
private Character(Submarine submarine, ushort id): base(submarine, id)
|
||||
{
|
||||
wallet = new Wallet(Option<Character>.Some(this));
|
||||
}
|
||||
|
||||
protected Character(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, RagdollParams ragdollParams = null)
|
||||
: base(null, id)
|
||||
: this(null, id)
|
||||
{
|
||||
this.Seed = seed;
|
||||
this.Prefab = prefab;
|
||||
|
||||
@@ -172,11 +172,21 @@ namespace Barotrauma
|
||||
SetCore(ContentPackageManager.WorkshopPackages.Core.FirstOrDefault(p => p.SteamWorkshopId == Core.SteamWorkshopId) ??
|
||||
ContentPackageManager.CorePackages.First());
|
||||
}
|
||||
SetRegular(Regular
|
||||
.Select(p => ContentPackageManager.RegularPackages.Contains(p)
|
||||
? p
|
||||
: ContentPackageManager.WorkshopPackages.Regular.FirstOrDefault(p2 => p2.SteamWorkshopId == p.SteamWorkshopId))
|
||||
.ToArray());
|
||||
|
||||
List<RegularPackage> newRegular = new List<RegularPackage>();
|
||||
foreach (var p in Regular)
|
||||
{
|
||||
if (ContentPackageManager.RegularPackages.Contains(p))
|
||||
{
|
||||
newRegular.Add(p);
|
||||
}
|
||||
else if (ContentPackageManager.WorkshopPackages.Regular.FirstOrDefault(p2
|
||||
=> p2.SteamWorkshopId == p.SteamWorkshopId) is { } newP)
|
||||
{
|
||||
newRegular.Add(newP);
|
||||
}
|
||||
}
|
||||
SetRegular(newRegular);
|
||||
}
|
||||
|
||||
public static void BackUp()
|
||||
|
||||
@@ -115,6 +115,24 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public void Remove() => Element.Remove();
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ContentXElement element && this == element;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(ContentPackage, Element);
|
||||
}
|
||||
|
||||
public static bool operator ==(in ContentXElement? a, in ContentXElement? b)
|
||||
{
|
||||
return a?.ContentPackage == b?.ContentPackage && a?.Element == b?.Element;
|
||||
}
|
||||
|
||||
public static bool operator !=(in ContentXElement? a, in ContentXElement? b) =>
|
||||
!(a == b);
|
||||
}
|
||||
|
||||
public static class ContentXElementExtensions
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Barotrauma
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum TransitionMode
|
||||
{
|
||||
@@ -146,4 +148,11 @@
|
||||
AlwaysStayConscious,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CharacterType
|
||||
{
|
||||
Bot = 0b01,
|
||||
Player = 0b10,
|
||||
Both = Bot | Player
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Barotrauma
|
||||
float difficultyMultiplier = 1 + level.Difficulty / 100f;
|
||||
baseExperienceGain *= difficultyMultiplier;
|
||||
|
||||
IEnumerable<Character> crewCharacters = GameSession.GetSessionCrewCharacters();
|
||||
IEnumerable<Character> crewCharacters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
|
||||
|
||||
// use multipliers here so that we can easily add them together without introducing multiplicative XP stacking
|
||||
var experienceGainMultiplier = new AbilityExperienceGainMultiplier(1f);
|
||||
@@ -380,7 +380,7 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(totalReward, GameAnalyticsManager.MoneySource.MissionReward, Prefab.Identifier.Value);
|
||||
|
||||
#if SERVER
|
||||
totalReward = DistributeRewardsToCrew(GetSalaryEligibleCrew(), totalReward);
|
||||
totalReward = DistributeRewardsToCrew(GameSession.GetSessionCrewCharacters(CharacterType.Player), totalReward);
|
||||
#endif
|
||||
if (totalReward > 0)
|
||||
{
|
||||
@@ -436,18 +436,6 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
public static IEnumerable<Character> GetSalaryEligibleCrew()
|
||||
{
|
||||
if (!(GameMain.GameSession.CrewManager is { } crewManager)) { return Array.Empty<Character>(); }
|
||||
|
||||
IEnumerable<Character> characters = crewManager.GetCharacters();
|
||||
#if SERVER
|
||||
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead).Concat(characters);
|
||||
#elif CLIENT
|
||||
return characters;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static int GetRewardDistibutionSum(IEnumerable<Character> crew, int rewardDistribution = 0) => crew.Sum(c => c.Wallet.RewardDistribution) + rewardDistribution;
|
||||
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly bool IsSideObjective;
|
||||
|
||||
public readonly bool RequireWreck;
|
||||
public readonly bool RequireWreck, RequireRuin;
|
||||
|
||||
/// <summary>
|
||||
/// The mission can only be received when travelling from a location of the first type to a location of the second type
|
||||
@@ -152,6 +152,7 @@ namespace Barotrauma
|
||||
AllowRetry = element.GetAttributeBool("allowretry", false);
|
||||
IsSideObjective = element.GetAttributeBool("sideobjective", false);
|
||||
RequireWreck = element.GetAttributeBool("requirewreck", false);
|
||||
RequireRuin = element.GetAttributeBool("requireruin", false);
|
||||
Commonness = element.GetAttributeInt("commonness", 1);
|
||||
if (element.GetAttribute("difficulty") != null)
|
||||
{
|
||||
|
||||
@@ -226,6 +226,7 @@ namespace Barotrauma
|
||||
|
||||
public void SetPurchasedItems(Dictionary<Identifier, List<PurchasedItem>> purchasedItems)
|
||||
{
|
||||
if (purchasedItems.Count == 0 && PurchasedItems.Count == 0) { return; }
|
||||
PurchasedItems.Clear();
|
||||
foreach (var entry in purchasedItems)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Barotrauma
|
||||
if (reputationChange > 0f)
|
||||
{
|
||||
float reputationGainMultiplier = 1f;
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters())
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
|
||||
{
|
||||
reputationGainMultiplier += character.GetStatValue(StatTypes.ReputationGainMultiplier);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
internal readonly struct WalletChangedEvent
|
||||
{
|
||||
public readonly Option<Character> Owner;
|
||||
public readonly Wallet Wallet;
|
||||
public readonly WalletInfo Info;
|
||||
public readonly WalletChangedData ChangedData;
|
||||
@@ -15,6 +16,7 @@ namespace Barotrauma
|
||||
Wallet = wallet;
|
||||
Info = info;
|
||||
ChangedData = changedData;
|
||||
Owner = wallet.Owner;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +111,8 @@ namespace Barotrauma
|
||||
// ReSharper disable ValueParameterNotUsed
|
||||
internal sealed class InvalidWallet : Wallet
|
||||
{
|
||||
public InvalidWallet(): base(Option<Character>.None()) { }
|
||||
|
||||
public override int Balance
|
||||
{
|
||||
get => 0;
|
||||
@@ -132,6 +136,8 @@ namespace Barotrauma
|
||||
AttrubuteNameRewardDistribution = "rewarddistribution",
|
||||
SaveElementName = "Wallet";
|
||||
|
||||
public readonly Option<Character> Owner;
|
||||
|
||||
private int balance;
|
||||
|
||||
public virtual int Balance
|
||||
@@ -148,9 +154,12 @@ namespace Barotrauma
|
||||
set => rewardDistribution = ClampRewardDistribution(value);
|
||||
}
|
||||
|
||||
public Wallet() { }
|
||||
public Wallet(Option<Character> owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
public Wallet(XElement element)
|
||||
public Wallet(Option<Character> owner, XElement element): this(owner)
|
||||
{
|
||||
balance = ClampBalance(element.GetAttributeInt(AttributeNameBalance, 0));
|
||||
rewardDistribution = ClampBalance(element.GetAttributeInt(AttrubuteNameRewardDistribution, 0));
|
||||
@@ -185,7 +194,7 @@ namespace Barotrauma
|
||||
SettingsChanged(balanceChanged: Option<int>.Some(-price), rewardChanged: Option<int>.None());
|
||||
}
|
||||
|
||||
public void SetRewardDistrubiton(int value)
|
||||
public void SetRewardDistribution(int value)
|
||||
{
|
||||
int oldValue = RewardDistribution;
|
||||
RewardDistribution = value;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma
|
||||
public int GetAddedMissionCount()
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters())
|
||||
foreach (Character character in GameSession.GetSessionCrewCharacters(CharacterType.Both))
|
||||
{
|
||||
count += (int)character.GetStatValue(StatTypes.ExtraMissionCount);
|
||||
}
|
||||
@@ -68,6 +68,15 @@ namespace Barotrauma
|
||||
|
||||
abstract partial class CampaignMode : GameMode
|
||||
{
|
||||
[NetworkSerialize]
|
||||
public struct SaveInfo : INetSerializableStruct
|
||||
{
|
||||
public string FilePath;
|
||||
public int SaveTime;
|
||||
public string SubmarineName;
|
||||
public string[] EnabledContentPackageNames;
|
||||
}
|
||||
|
||||
public const int MaxMoney = int.MaxValue / 2; //about 1 billion
|
||||
public const int InitialMoney = 8500;
|
||||
|
||||
@@ -180,13 +189,37 @@ namespace Barotrauma
|
||||
protected CampaignMode(GameModePreset preset)
|
||||
: base(preset)
|
||||
{
|
||||
Bank = new Wallet
|
||||
Bank = new Wallet(Option<Character>.None())
|
||||
{
|
||||
Balance = InitialMoney
|
||||
};
|
||||
|
||||
CargoManager = new CargoManager(this);
|
||||
MedicalClinic = new MedicalClinic(this);
|
||||
Identifier messageIdentifier = new Identifier("money");
|
||||
|
||||
#if CLIENT
|
||||
OnMoneyChanged.RegisterOverwriteExisting(new Identifier("CampaignMoneyChangeNotification"), e =>
|
||||
{
|
||||
if (!(e.ChangedData.BalanceChanged is Some<int> { Value: var changed })) { return; }
|
||||
|
||||
bool isGain = changed > 0;
|
||||
|
||||
Color clr = isGain ? GUIStyle.Yellow : GUIStyle.Red;
|
||||
|
||||
switch (e.Owner)
|
||||
{
|
||||
case Some<Character> { Value: var owner}:
|
||||
owner.AddMessage(FormatMessage(), clr, playSound: Character.Controlled == owner, messageIdentifier, changed);
|
||||
break;
|
||||
case None<Character> _ when IsSinglePlayer:
|
||||
Character.Controlled?.AddMessage(FormatMessage(), clr, playSound: true, messageIdentifier, changed);
|
||||
break;
|
||||
}
|
||||
|
||||
string FormatMessage() => TextManager.GetWithVariable(isGain ? "moneygainformat" : "moneyloseformat", "[money]", TextManager.FormatCurrency(Math.Abs(changed))).ToString();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
public virtual Wallet GetWallet(Client client = null)
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Barotrauma
|
||||
LoadStats(subElement);
|
||||
break;
|
||||
case Wallet.LowerCaseSaveElementName:
|
||||
Bank = new Wallet(subElement);
|
||||
Bank = new Wallet(Option<Character>.None(), subElement);
|
||||
break;
|
||||
#if SERVER
|
||||
case "savedexperiencepoints":
|
||||
@@ -183,7 +183,7 @@ namespace Barotrauma
|
||||
int oldMoney = element.GetAttributeInt("money", 0);
|
||||
if (oldMoney > 0)
|
||||
{
|
||||
Bank = new Wallet
|
||||
Bank = new Wallet(Option<Character>.None())
|
||||
{
|
||||
Balance = oldMoney
|
||||
};
|
||||
|
||||
@@ -735,14 +735,40 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public static IEnumerable<Character> GetSessionCrewCharacters()
|
||||
/// <summary>
|
||||
/// Returns a list of crew characters currently in the game with a given filter.
|
||||
/// </summary>
|
||||
/// <param name="type">Character type filter</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// In singleplayer mode the CharacterType.Player returns the currently controlled player.
|
||||
/// </remarks>
|
||||
public static ImmutableHashSet<Character> GetSessionCrewCharacters(CharacterType type)
|
||||
{
|
||||
if (!(GameMain.GameSession.CrewManager is { } crewManager)) { return ImmutableHashSet<Character>.Empty; }
|
||||
|
||||
IEnumerable<Character> players;
|
||||
IEnumerable<Character> bots;
|
||||
HashSet<Character> characters = new HashSet<Character>();
|
||||
|
||||
#if SERVER
|
||||
return GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
|
||||
#else
|
||||
if (GameMain.GameSession?.CrewManager is null) { return Enumerable.Empty<Character>(); }
|
||||
return GameMain.GameSession.CrewManager.GetCharacters().Where(c => c?.Info != null && !c.IsDead);
|
||||
players = GameMain.Server.ConnectedClients.Select(c => c.Character).Where(c => c?.Info != null && !c.IsDead);
|
||||
bots = crewManager.GetCharacters().Where(c => !c.IsRemotePlayer);
|
||||
#elif CLIENT
|
||||
players = crewManager.GetCharacters().Where(c => c.IsPlayer);
|
||||
bots = crewManager.GetCharacters().Where(c => c.IsBot);
|
||||
#endif
|
||||
if (type.HasFlag(CharacterType.Bot))
|
||||
{
|
||||
foreach (Character bot in bots) { characters.Add(bot); }
|
||||
}
|
||||
|
||||
if (type.HasFlag(CharacterType.Player))
|
||||
{
|
||||
foreach (Character player in players) { characters.Add(player); }
|
||||
}
|
||||
|
||||
return characters.ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public void EndRound(string endMessage, List<TraitorMissionResult>? traitorResults = null, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
|
||||
@@ -754,7 +780,7 @@ namespace Barotrauma
|
||||
|
||||
try
|
||||
{
|
||||
ImmutableArray<Character> crewCharacters = GetSessionCrewCharacters().ToImmutableArray();
|
||||
ImmutableHashSet<Character> crewCharacters = GetSessionCrewCharacters(CharacterType.Both);
|
||||
|
||||
int prevMoney = GetAmountOfMoney(crewCharacters);
|
||||
|
||||
@@ -898,7 +924,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in GetSessionCrewCharacters())
|
||||
foreach (Character c in GetSessionCrewCharacters(CharacterType.Both))
|
||||
{
|
||||
foreach (var itemSelectedDuration in c.ItemSelectedDurations)
|
||||
{
|
||||
@@ -948,25 +974,25 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsCompatibleWithEnabledContentPackages(IList<string> contentPackagePaths, out LocalizedString errorMsg)
|
||||
public static bool IsCompatibleWithEnabledContentPackages(IList<string> contentPackageNames, out LocalizedString errorMsg)
|
||||
{
|
||||
errorMsg = "";
|
||||
//no known content packages, must be an older save file
|
||||
if (!contentPackagePaths.Any()) { return true; }
|
||||
if (!contentPackageNames.Any()) { return true; }
|
||||
|
||||
List<string> missingPackages = new List<string>();
|
||||
foreach (string packagePath in contentPackagePaths)
|
||||
foreach (string packageName in contentPackageNames)
|
||||
{
|
||||
if (!ContentPackageManager.EnabledPackages.All.Any(cp => cp.Path == packagePath))
|
||||
if (!ContentPackageManager.EnabledPackages.All.Any(cp => cp.NameMatches(packageName)))
|
||||
{
|
||||
missingPackages.Add(packagePath);
|
||||
missingPackages.Add(packageName);
|
||||
}
|
||||
}
|
||||
List<string> excessPackages = new List<string>();
|
||||
foreach (ContentPackage cp in ContentPackageManager.EnabledPackages.All)
|
||||
{
|
||||
if (!cp.HasMultiplayerSyncedContent) { continue; }
|
||||
if (!contentPackagePaths.Any(p => p == cp.Path))
|
||||
if (!contentPackageNames.Any(p => cp.NameMatches(p)))
|
||||
{
|
||||
excessPackages.Add(cp.Name);
|
||||
}
|
||||
@@ -976,9 +1002,9 @@ namespace Barotrauma
|
||||
if (missingPackages.Count == 0 && missingPackages.Count == 0)
|
||||
{
|
||||
var enabledPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).ToImmutableArray();
|
||||
for (int i = 0; i < contentPackagePaths.Count && i < enabledPackages.Length; i++)
|
||||
for (int i = 0; i < contentPackageNames.Count && i < enabledPackages.Length; i++)
|
||||
{
|
||||
if (contentPackagePaths[i] != enabledPackages[i].Path)
|
||||
if (!enabledPackages[i].NameMatches(contentPackageNames[i]))
|
||||
{
|
||||
orderMismatch = true;
|
||||
break;
|
||||
@@ -1009,7 +1035,7 @@ namespace Barotrauma
|
||||
if (orderMismatch)
|
||||
{
|
||||
if (!errorMsg.IsNullOrEmpty()) { errorMsg += "\n"; }
|
||||
errorMsg += TextManager.GetWithVariable("campaignmode.contentpackageordermismatch", "[loadorder]", string.Join(", ", contentPackagePaths));
|
||||
errorMsg += TextManager.GetWithVariable("campaignmode.contentpackageordermismatch", "[loadorder]", string.Join(", ", contentPackageNames));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1040,8 +1066,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
if (Map != null) { rootElement.Add(new XAttribute("mapseed", Map.Seed)); }
|
||||
rootElement.Add(new XAttribute("selectedcontentpackages",
|
||||
string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Path))));
|
||||
rootElement.Add(new XAttribute("selectedcontentpackagenames",
|
||||
string.Join("|", ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent).Select(cp => cp.Name.Replace("|", @"\|")))));
|
||||
|
||||
((CampaignMode)GameMode).Save(doc.Root);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -13,6 +12,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
[Editable]
|
||||
public LimbType LimbType { get; set; }
|
||||
|
||||
[Editable]
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
partial class Controller : ItemComponent, IServerSerializable
|
||||
{
|
||||
//where the limbs of the user should be positioned when using the controller
|
||||
private readonly List<LimbPos> limbPositions;
|
||||
private readonly List<LimbPos> limbPositions = new List<LimbPos>();
|
||||
|
||||
private Direction dir;
|
||||
|
||||
@@ -117,38 +117,9 @@ namespace Barotrauma.Items.Components
|
||||
public Controller(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
limbPositions = new List<LimbPos>();
|
||||
|
||||
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
|
||||
|
||||
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name != "limbposition") { continue; }
|
||||
string limbStr = subElement.GetAttributeString("limb", "");
|
||||
if (!Enum.TryParse(subElement.GetAttribute("limb").Value, out LimbType limbType))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LimbPos limbPos = new LimbPos(limbType,
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
subElement.GetAttributeBool("allowusinglimb", false));
|
||||
limbPositions.Add(limbPos);
|
||||
if (!limbPos.AllowUsingLimb)
|
||||
{
|
||||
if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
|
||||
limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
|
||||
{
|
||||
AllowAiming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LoadLimbPositions(element);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -529,5 +500,63 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
partial void HideHUDs(bool value);
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
return SaveLimbPositions(base.Save(parentElement));
|
||||
}
|
||||
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
if (GameMain.GameSession?.GameMode?.Preset == GameModePreset.TestMode)
|
||||
{
|
||||
LoadLimbPositions(componentElement);
|
||||
}
|
||||
}
|
||||
|
||||
private XElement SaveLimbPositions(XElement element)
|
||||
{
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
foreach (var limbPos in limbPositions)
|
||||
{
|
||||
element.Add(new XElement("limbposition",
|
||||
new XAttribute("limb", limbPos.LimbType),
|
||||
new XAttribute("position", XMLExtensions.Vector2ToString(limbPos.Position)),
|
||||
new XAttribute("allowusinglimb", limbPos.AllowUsingLimb)));
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private void LoadLimbPositions(XElement element)
|
||||
{
|
||||
limbPositions.Clear();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name != "limbposition") { continue; }
|
||||
string limbStr = subElement.GetAttributeString("limb", "");
|
||||
if (!Enum.TryParse(subElement.GetAttribute("limb").Value, out LimbType limbType))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LimbPos limbPos = new LimbPos(limbType,
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
subElement.GetAttributeBool("allowusinglimb", false));
|
||||
limbPositions.Add(limbPos);
|
||||
if (!limbPos.AllowUsingLimb)
|
||||
{
|
||||
if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
|
||||
limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
|
||||
{
|
||||
AllowAiming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float progressState;
|
||||
|
||||
private readonly Dictionary<uint, int> fabricationLimits = new Dictionary<uint, int>();
|
||||
|
||||
public Fabricator(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -105,6 +107,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
fabricationRecipes.Add(recipe.RecipeHash, recipe);
|
||||
if (recipe.FabricationLimitMax >= 0)
|
||||
{
|
||||
fabricationLimits.Add(recipe.RecipeHash, Rand.Range(recipe.FabricationLimitMin, recipe.FabricationLimitMax + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
|
||||
@@ -244,7 +250,7 @@ namespace Barotrauma.Items.Components
|
||||
itemList.Enabled = true;
|
||||
if (activateButton != null)
|
||||
{
|
||||
activateButton.Text = TextManager.Get("FabricatorCreate");
|
||||
activateButton.Text = TextManager.Get(CreateButtonText);
|
||||
}
|
||||
#endif
|
||||
fabricatedItem = null;
|
||||
@@ -400,8 +406,22 @@ namespace Barotrauma.Items.Components
|
||||
quality = GetFabricatedItemQuality(fabricatedItem, user);
|
||||
}
|
||||
|
||||
int amount = (int)fabricationitemAmount.Value;
|
||||
if (fabricationLimits.ContainsKey(fabricatedItem.RecipeHash))
|
||||
{
|
||||
if (amount > fabricationLimits[fabricatedItem.RecipeHash])
|
||||
{
|
||||
amount = fabricationLimits[fabricatedItem.RecipeHash];
|
||||
fabricationLimits[fabricatedItem.RecipeHash] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fabricationLimits[fabricatedItem.RecipeHash] -= amount;
|
||||
}
|
||||
}
|
||||
|
||||
var tempUser = user;
|
||||
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
float outCondition = fabricatedItem.OutCondition;
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
||||
@@ -535,6 +555,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (fabricationLimits.TryGetValue(fabricableItem.RecipeHash, out int amount) && amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return fabricableItem.RequiredItems.All(requiredItem =>
|
||||
{
|
||||
int availablePrefabsAmount = 0;
|
||||
@@ -698,7 +723,6 @@ namespace Barotrauma.Items.Components
|
||||
componentElement.Add(new XAttribute("fabricateditemidentifier", fabricatedItem.TargetItem.Identifier));
|
||||
componentElement.Add(new XAttribute("savedtimeuntilready", timeUntilReady.ToString("G", CultureInfo.InvariantCulture)));
|
||||
componentElement.Add(new XAttribute("savedrequiredtime", requiredTime.ToString("G", CultureInfo.InvariantCulture)));
|
||||
|
||||
}
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -114,7 +115,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly Quality qualityComponent;
|
||||
|
||||
private readonly Queue<float> impactQueue = new Queue<float>();
|
||||
private readonly ConcurrentQueue<float> impactQueue = new ConcurrentQueue<float>();
|
||||
|
||||
//a dictionary containing lists of the status effects in all the components of the item
|
||||
private readonly bool[] hasStatusEffectsOfType;
|
||||
@@ -1695,9 +1696,8 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
while (impactQueue.Count > 0)
|
||||
while (impactQueue.TryDequeue(out float impact))
|
||||
{
|
||||
float impact = impactQueue.Dequeue();
|
||||
HandleCollision(impact);
|
||||
}
|
||||
|
||||
@@ -1933,10 +1933,7 @@ namespace Barotrauma
|
||||
if (contact.FixtureA.Body == f1.Body) { normal = -normal; }
|
||||
float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
|
||||
|
||||
lock (impactQueue)
|
||||
{
|
||||
impactQueue.Enqueue(impact);
|
||||
}
|
||||
impactQueue.Enqueue(impact);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,11 @@ namespace Barotrauma
|
||||
public readonly uint RecipeHash;
|
||||
public readonly int Amount;
|
||||
|
||||
/// <summary>
|
||||
/// How many of this item the fabricator can create (< 0 = unlimited)
|
||||
/// </summary>
|
||||
public readonly int FabricationLimitMin, FabricationLimitMax;
|
||||
|
||||
public FabricationRecipe(XElement element, Identifier itemPrefab)
|
||||
{
|
||||
TargetItemPrefabIdentifier = itemPrefab;
|
||||
@@ -141,6 +146,10 @@ namespace Barotrauma
|
||||
RequiresRecipe = element.GetAttributeBool("requiresrecipe", false);
|
||||
Amount = element.GetAttributeInt("amount", 1);
|
||||
|
||||
int limitDefault = element.GetAttributeInt("fabricationlimit", -1);
|
||||
FabricationLimitMin = element.GetAttributeInt(nameof(FabricationLimitMin), limitDefault);
|
||||
FabricationLimitMax = element.GetAttributeInt(nameof(FabricationLimitMax), limitDefault);
|
||||
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -973,7 +982,11 @@ namespace Barotrauma
|
||||
|
||||
public int? GetMinPrice()
|
||||
{
|
||||
int? minPrice = StorePrices.Values.Min(p => p.Price);
|
||||
int? minPrice = null;
|
||||
if (StorePrices != null && StorePrices.Any())
|
||||
{
|
||||
minPrice = StorePrices.Values.Min(p => p.Price);
|
||||
}
|
||||
if (minPrice.HasValue)
|
||||
{
|
||||
if (DefaultPrice != null)
|
||||
|
||||
@@ -831,7 +831,13 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<Point> ruinPositions = new List<Point>();
|
||||
for (int i = 0; i < GenerationParams.RuinCount; i++)
|
||||
int ruinCount = GenerationParams.RuinCount;
|
||||
if (GameMain.GameSession?.GameMode?.Missions.Any(m => m.Prefab.RequireRuin) ?? false)
|
||||
{
|
||||
ruinCount = Math.Max(ruinCount, 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ruinCount; i++)
|
||||
{
|
||||
Point ruinSize = new Point(5000);
|
||||
int limitLeft = Math.Max(startPosition.X, ruinSize.X / 2);
|
||||
|
||||
@@ -387,14 +387,14 @@ namespace Barotrauma
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(3, IsPropertySaveable.Yes, description: "Minimum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
[Serialize(10, IsPropertySaveable.Yes, description: "Minimum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int AbyssResourceClustersMin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(20, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
[Serialize(50, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int AbyssResourceClustersMax
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace Barotrauma
|
||||
}
|
||||
DailySpecials.Clear();
|
||||
int extraSpecialSalesCount = Location.GetExtraSpecialSalesCount();
|
||||
for (int i = 0; i < DailySpecialsCount + extraSpecialSalesCount; i++)
|
||||
for (int i = 0; i < Location.DailySpecialsCount + extraSpecialSalesCount; i++)
|
||||
{
|
||||
if (availableStock.None()) { break; }
|
||||
var item = ToolBox.SelectWeightedRandom(availableStock.Keys.ToList(), availableStock.Values.ToList(), Rand.RandSync.Unsynced);
|
||||
@@ -266,7 +266,7 @@ namespace Barotrauma
|
||||
availableStock.Remove(item);
|
||||
}
|
||||
RequestedGoods.Clear();
|
||||
for (int i = 0; i < RequestedGoodsCount; i++)
|
||||
for (int i = 0; i < Location.RequestedGoodsCount; i++)
|
||||
{
|
||||
var item = ItemPrefab.Prefabs.GetRandom(p =>
|
||||
p.CanBeSold && !RequestedGoods.Contains(p) &&
|
||||
@@ -359,8 +359,8 @@ namespace Barotrauma
|
||||
/// How many map progress steps it takes before the discounts should be updated.
|
||||
/// </summary>
|
||||
private const int SpecialsUpdateInterval = 3;
|
||||
private const int DailySpecialsCount = 3;
|
||||
private const int RequestedGoodsCount = 3;
|
||||
private int DailySpecialsCount => Type.DailySpecialsCount;
|
||||
private int RequestedGoodsCount => Type.RequestedGoodsCount;
|
||||
private int StepsSinceSpecialsUpdated { get; set; }
|
||||
public HashSet<Identifier> StoreIdentifiers { get; } = new HashSet<Identifier>();
|
||||
|
||||
@@ -1226,7 +1226,7 @@ namespace Barotrauma
|
||||
|
||||
public int GetExtraSpecialSalesCount()
|
||||
{
|
||||
var characters = GameSession.GetSessionCrewCharacters();
|
||||
var characters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
|
||||
if (!characters.Any()) { return 0; }
|
||||
return characters.Max(c => (int)c.GetStatValue(StatTypes.ExtraSpecialSalesCount));
|
||||
}
|
||||
@@ -1252,7 +1252,7 @@ namespace Barotrauma
|
||||
Discovered = true;
|
||||
if (checkTalents)
|
||||
{
|
||||
GameSession.GetSessionCrewCharacters().ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new AbilityLocation(this)));
|
||||
GameSession.GetSessionCrewCharacters(CharacterType.Both).ForEach(c => c.CheckTalents(AbilityEffectType.OnLocationDiscovered, new AbilityLocation(this)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -21,8 +19,9 @@ namespace Barotrauma
|
||||
//<name, commonness>
|
||||
private readonly ImmutableArray<(Identifier Name, float Commonness)> hireableJobs;
|
||||
private readonly float totalHireableWeight;
|
||||
|
||||
public Dictionary<int, float> CommonnessPerZone = new Dictionary<int, float>();
|
||||
|
||||
public readonly Dictionary<int, float> CommonnessPerZone = new Dictionary<int, float>();
|
||||
public readonly Dictionary<int, int> MinCountPerZone = new Dictionary<int, int>();
|
||||
|
||||
public readonly LocalizedString Name;
|
||||
|
||||
@@ -65,7 +64,7 @@ namespace Barotrauma
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public string ReplaceInRadiation { get; }
|
||||
|
||||
public Sprite Sprite { get; private set; }
|
||||
@@ -86,6 +85,8 @@ namespace Barotrauma
|
||||
/// In percentages
|
||||
/// </summary>
|
||||
public int StorePriceModifierRange { get; } = 5;
|
||||
public int DailySpecialsCount { get; } = 1;
|
||||
public int RequestedGoodsCount { get; } = 1;
|
||||
|
||||
public List<StoreBalanceStatus> StoreBalanceStatuses { get; } = new List<StoreBalanceStatus>()
|
||||
{
|
||||
@@ -144,7 +145,7 @@ namespace Barotrauma
|
||||
names = new List<string>() { "Name file not found" };
|
||||
}
|
||||
|
||||
string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", new string[] { "" });
|
||||
string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", Array.Empty<string>());
|
||||
foreach (string commonnessPerZoneStr in commonnessPerZoneStrs)
|
||||
{
|
||||
string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(':');
|
||||
@@ -152,12 +153,26 @@ namespace Barotrauma
|
||||
!int.TryParse(splitCommonnessPerZone[0].Trim(), out int zoneIndex) ||
|
||||
!float.TryParse(splitCommonnessPerZone[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float zoneCommonness))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone0index: zone0commonness, zone1index: zone1commonness\"");
|
||||
DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Identifier + "\" - commonness should be given in the format \"zone1index: zone1commonness, zone2index: zone2commonness\"");
|
||||
break;
|
||||
}
|
||||
CommonnessPerZone[zoneIndex] = zoneCommonness;
|
||||
}
|
||||
|
||||
string[] minCountPerZoneStrs = element.GetAttributeStringArray("mincountperzone", Array.Empty<string>());
|
||||
foreach (string minCountPerZoneStr in minCountPerZoneStrs)
|
||||
{
|
||||
string[] splitMinCountPerZone = minCountPerZoneStr.Split(':');
|
||||
if (splitMinCountPerZone.Length != 2 ||
|
||||
!int.TryParse(splitMinCountPerZone[0].Trim(), out int zoneIndex) ||
|
||||
!int.TryParse(splitMinCountPerZone[1].Trim(), out int minCount))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to read minimum count values for location type \"" + Identifier + "\" - minimum counts should be given in the format \"zone1index: zone1mincount, zone2index: zone2mincount\"");
|
||||
break;
|
||||
}
|
||||
MinCountPerZone[zoneIndex] = minCount;
|
||||
}
|
||||
|
||||
var hireableJobs = new List<(Identifier, float)>();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
@@ -205,6 +220,8 @@ namespace Barotrauma
|
||||
StoreBalanceStatuses.Add(new StoreBalanceStatus(percentage, modifier, color));
|
||||
}
|
||||
}
|
||||
DailySpecialsCount = subElement.GetAttributeInt("dailyspecialscount", DailySpecialsCount);
|
||||
RequestedGoodsCount = subElement.GetAttributeInt("requestedgoodscount", RequestedGoodsCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,16 +218,24 @@ namespace Barotrauma
|
||||
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (location.Type.Identifier != "city" &&
|
||||
location.Type.Identifier != "outpost")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (location.Type.Identifier != "outpost") { continue; }
|
||||
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
|
||||
{
|
||||
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
|
||||
}
|
||||
}
|
||||
//if no outpost was found (using a mod that replaces the outpost location type?), find any type of outpost
|
||||
if (CurrentLocation == null)
|
||||
{
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (!location.Type.HasOutpost) { continue; }
|
||||
if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X)
|
||||
{
|
||||
CurrentLocation = StartLocation = furthestDiscoveredLocation = location;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
|
||||
|
||||
CurrentLocation.Discover(true);
|
||||
@@ -273,6 +281,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
voronoiSites.Clear();
|
||||
Dictionary<int, List<Location>> locationsPerZone = new Dictionary<int, List<Location>>();
|
||||
foreach (GraphEdge edge in edges)
|
||||
{
|
||||
if (edge.Point1 == edge.Point2) { continue; }
|
||||
@@ -301,7 +310,24 @@ namespace Barotrauma
|
||||
Vector2 position = points[positionIndex];
|
||||
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) { position = points[1 - positionIndex]; }
|
||||
int zone = GetZoneIndex(position.X);
|
||||
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.ServerAndClient), requireOutpost: false, existingLocations: Locations);
|
||||
if (!locationsPerZone.ContainsKey(zone))
|
||||
{
|
||||
locationsPerZone[zone] = new List<Location>();
|
||||
}
|
||||
|
||||
LocationType forceLocationType = null;
|
||||
foreach (LocationType locationType in LocationType.Prefabs.OrderBy(lt => lt.Identifier))
|
||||
{
|
||||
if (locationType.MinCountPerZone.TryGetValue(zone, out int minCount) && locationsPerZone[zone].Count(l => l.Type == locationType) < minCount)
|
||||
{
|
||||
forceLocationType = locationType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newLocations[i] = Location.CreateRandom(position, zone, Rand.GetRNG(Rand.RandSync.ServerAndClient),
|
||||
requireOutpost: false, forceLocationType: forceLocationType, existingLocations: Locations);
|
||||
locationsPerZone[zone].Add(newLocations[i]);
|
||||
Locations.Add(newLocations[i]);
|
||||
}
|
||||
|
||||
@@ -448,8 +474,7 @@ namespace Barotrauma
|
||||
Connections[i].Locations[1];
|
||||
if (!leftMostLocation.Type.HasOutpost || leftMostLocation.Type.Identifier == "abandoned")
|
||||
{
|
||||
#warning TODO: determinism?
|
||||
leftMostLocation.ChangeType(LocationType.Prefabs.First(lt => lt.HasOutpost && lt.Identifier != "abandoned"));
|
||||
leftMostLocation.ChangeType(LocationType.Prefabs.OrderBy(lt => lt.Identifier).First(lt => lt.HasOutpost && lt.Identifier != "abandoned"));
|
||||
}
|
||||
leftMostLocation.IsGateBetweenBiomes = true;
|
||||
Connections[i].Locked = true;
|
||||
|
||||
@@ -752,6 +752,7 @@ namespace Barotrauma
|
||||
bool solutionFound = false;
|
||||
foreach (PlacedModule module in movableModules)
|
||||
{
|
||||
if (module.ThisGap.ConnectedDoor == null && module.PreviousGap.ConnectedDoor == null) { continue; }
|
||||
Vector2 moveDir = GetMoveDir(module.ThisGapPosition);
|
||||
Vector2 moveStep = moveDir * 50.0f;
|
||||
Vector2 currentMove = Vector2.Zero;
|
||||
@@ -1093,6 +1094,10 @@ namespace Barotrauma
|
||||
}
|
||||
thisWayPoint.Remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {GetOpposingGapPosition(module.ThisGapPosition).ToString().ToLower()} gap of the module \"{module.PreviousModule.Info.Name}\".");
|
||||
}
|
||||
|
||||
gapToRemove.ConnectedDoor?.Item.Remove();
|
||||
if (hallwayLength <= 1.0f) { gapToRemove?.Remove(); }
|
||||
|
||||
@@ -28,8 +28,6 @@ namespace Barotrauma
|
||||
|
||||
partial class SubmarineInfo : IDisposable
|
||||
{
|
||||
public const string SavePath = "Submarines";
|
||||
|
||||
private static List<SubmarineInfo> savedSubmarines = new List<SubmarineInfo>();
|
||||
public static IEnumerable<SubmarineInfo> SavedSubmarines => savedSubmarines;
|
||||
|
||||
@@ -578,58 +576,14 @@ namespace Barotrauma
|
||||
if (File.Exists(savedSubmarines[i].FilePath))
|
||||
{
|
||||
bool isDownloadedSub = Path.GetFullPath(Path.GetDirectoryName(savedSubmarines[i].FilePath)) == Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
|
||||
bool isInSubmarinesFolder = Path.GetFullPath(Path.GetDirectoryName(savedSubmarines[i].FilePath)) == Path.GetFullPath(SavePath);
|
||||
bool isInContentPackage = contentPackageSubs.Any(f => f.Path == savedSubmarines[i].FilePath);
|
||||
if (isDownloadedSub) { continue; }
|
||||
if (savedSubmarines[i].LastModifiedTime == File.GetLastWriteTime(savedSubmarines[i].FilePath) && (isInSubmarinesFolder || isInContentPackage)) { continue; }
|
||||
if (savedSubmarines[i].LastModifiedTime == File.GetLastWriteTime(savedSubmarines[i].FilePath) && isInContentPackage) { continue; }
|
||||
}
|
||||
savedSubmarines[i].Dispose();
|
||||
}
|
||||
|
||||
if (!Directory.Exists(SavePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(SavePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Directory \"" + SavePath + "\" not found and creating the directory failed.", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<string> filePaths;
|
||||
string[] subDirectories;
|
||||
|
||||
try
|
||||
{
|
||||
filePaths = Directory.GetFiles(SavePath).ToList();
|
||||
subDirectories = Directory.GetDirectories(SavePath).Where(s =>
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(s);
|
||||
return !dir.Attributes.HasFlag(System.IO.FileAttributes.Hidden) && !dir.Name.StartsWith(".");
|
||||
}).ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't open directory \"" + SavePath + "\"!", e);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string subDirectory in subDirectories)
|
||||
{
|
||||
try
|
||||
{
|
||||
filePaths.AddRange(Directory.GetFiles(subDirectory).ToList());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't open subdirectory \"" + subDirectory + "\"!", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<string> filePaths = new List<string>();
|
||||
foreach (BaseSubFile subFile in contentPackageSubs)
|
||||
{
|
||||
if (!filePaths.Any(fp => fp == subFile.Path))
|
||||
@@ -643,34 +597,7 @@ namespace Barotrauma
|
||||
foreach (string path in filePaths)
|
||||
{
|
||||
var subInfo = new SubmarineInfo(path);
|
||||
if (subInfo.IsFileCorrupted)
|
||||
{
|
||||
#if CLIENT
|
||||
if (DebugConsole.IsOpen) { DebugConsole.Toggle(); }
|
||||
var deleteSubPrompt = new GUIMessageBox(
|
||||
TextManager.Get("Error"),
|
||||
TextManager.GetWithVariable("SubLoadError", "[subname]", subInfo.Name) + "\n" +
|
||||
TextManager.GetWithVariable("DeleteFileVerification", "[filename]", subInfo.Name),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
||||
|
||||
string filePath = path;
|
||||
deleteSubPrompt.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to delete file \"{filePath}\".", e);
|
||||
}
|
||||
deleteSubPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
deleteSubPrompt.Buttons[1].OnClicked += deleteSubPrompt.Close;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
if (!subInfo.IsFileCorrupted)
|
||||
{
|
||||
savedSubmarines.Add(subInfo);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ namespace Barotrauma.Networking
|
||||
ManageSettings = 0x200,
|
||||
ManagePermissions = 0x400,
|
||||
KarmaImmunity = 0x800,
|
||||
BuyItems = 0x1000,
|
||||
ManageMoney = 0x1000,
|
||||
SellInventoryItems = 0x2000,
|
||||
SellSubItems = 0x4000,
|
||||
CampaignStore = 0x8000,
|
||||
All = 0xFFFF
|
||||
ManageMap = 0x8000,
|
||||
ManageHires = 0x10000,
|
||||
All = 0x1FFFF
|
||||
}
|
||||
|
||||
class PermissionPreset
|
||||
|
||||
@@ -116,7 +116,8 @@ namespace Barotrauma.Networking
|
||||
StartRound,
|
||||
PurchaseAndSwitchSub,
|
||||
PurchaseSub,
|
||||
SwitchSub
|
||||
SwitchSub,
|
||||
TransferMoney
|
||||
}
|
||||
|
||||
public enum ReadyCheckState
|
||||
@@ -179,11 +180,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
protected ServerSettings serverSettings;
|
||||
|
||||
public Voting Voting { get; protected set; }
|
||||
|
||||
protected TimeSpan updateInterval;
|
||||
protected DateTime updateTimer;
|
||||
|
||||
public int EndVoteCount, EndVoteMax, SubmarineVoteYesCount, SubmarineVoteNoCount, SubmarineVoteMax;
|
||||
|
||||
protected bool gameStarted;
|
||||
|
||||
protected RespawnManager respawnManager;
|
||||
|
||||
@@ -278,8 +278,6 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
ServerLog = new ServerLog(serverName);
|
||||
|
||||
Voting = new Voting();
|
||||
|
||||
Whitelist = new WhiteList();
|
||||
BanList = new BanList();
|
||||
|
||||
@@ -378,8 +376,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
public ServerLog ServerLog;
|
||||
|
||||
public Voting Voting;
|
||||
|
||||
public Dictionary<Identifier, bool> MonsterEnabled { get; private set; }
|
||||
|
||||
public const int MaxExtraCargoItemsOfType = 10;
|
||||
@@ -577,34 +573,20 @@ namespace Barotrauma.Networking
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowVoteKick
|
||||
{
|
||||
get
|
||||
{
|
||||
return Voting.AllowVoteKick;
|
||||
}
|
||||
set
|
||||
{
|
||||
Voting.AllowVoteKick = value;
|
||||
}
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowEndVoting
|
||||
{
|
||||
get
|
||||
{
|
||||
return Voting.AllowEndVoting;
|
||||
}
|
||||
set
|
||||
{
|
||||
Voting.AllowEndVoting = value;
|
||||
}
|
||||
get; set;
|
||||
}
|
||||
|
||||
private bool allowRespawn;
|
||||
[Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool AllowRespawn
|
||||
{
|
||||
get { return allowRespawn; ; }
|
||||
get { return allowRespawn; }
|
||||
set
|
||||
{
|
||||
if (allowRespawn == value) { return; }
|
||||
@@ -779,7 +761,7 @@ namespace Barotrauma.Networking
|
||||
set
|
||||
{
|
||||
subSelectionMode = value;
|
||||
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
|
||||
AllowSubVoting = subSelectionMode == SelectionMode.Vote;
|
||||
ServerDetailsChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -792,7 +774,7 @@ namespace Barotrauma.Networking
|
||||
set
|
||||
{
|
||||
modeSelectionMode = value;
|
||||
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
|
||||
AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
|
||||
ServerDetailsChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -807,14 +789,14 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
[Serialize(0.6f, IsPropertySaveable.Yes)]
|
||||
public float SubmarineVoteRequiredRatio
|
||||
public float VoteRequiredRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(30f, IsPropertySaveable.Yes)]
|
||||
public float SubmarineVoteTimeout
|
||||
public float VoteTimeout
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -928,6 +910,59 @@ namespace Barotrauma.Networking
|
||||
set { maxMissionCount = MathHelper.Clamp(value, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit); }
|
||||
}
|
||||
|
||||
private bool allowSubVoting;
|
||||
//Don't serialize: the value is set based on SubSelectionMode
|
||||
public bool AllowSubVoting
|
||||
{
|
||||
get { return allowSubVoting; }
|
||||
set
|
||||
{
|
||||
if (value == allowSubVoting) { return; }
|
||||
allowSubVoting = value;
|
||||
#if CLIENT
|
||||
GameMain.NetLobbyScreen.SubList.Enabled = value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectSub));
|
||||
var subVotesLabel = GameMain.NetLobbyScreen.Frame.FindChild("subvotes", true) as GUITextBlock;
|
||||
subVotesLabel.Visible = value;
|
||||
var subVisButton = GameMain.NetLobbyScreen.SubVisibilityButton;
|
||||
subVisButton.RectTransform.AbsoluteOffset
|
||||
= new Point(value ? (int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
|
||||
|
||||
GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Sub);
|
||||
GameMain.NetLobbyScreen.SubList.Deselect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private bool allowModeVoting;
|
||||
//Don't serialize: the value is set based on ModeSelectionMode
|
||||
public bool AllowModeVoting
|
||||
{
|
||||
get { return allowModeVoting; }
|
||||
set
|
||||
{
|
||||
if (value == allowModeVoting) { return; }
|
||||
allowModeVoting = value;
|
||||
#if CLIENT
|
||||
GameMain.NetLobbyScreen.ModeList.Enabled =
|
||||
value ||
|
||||
(GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectMode));
|
||||
GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
|
||||
// Disable modes that cannot be voted on
|
||||
foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
|
||||
{
|
||||
if (guiComponent is GUIFrame frame)
|
||||
{
|
||||
frame.CanBeFocused = !allowModeVoting || ((GameModePreset)frame.UserData).Votable;
|
||||
}
|
||||
}
|
||||
GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Mode);
|
||||
GameMain.NetLobbyScreen.ModeList.Deselect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Voting
|
||||
{
|
||||
private bool allowSubVoting, allowModeVoting;
|
||||
|
||||
public bool AllowVoteKick = true;
|
||||
|
||||
public bool AllowEndVoting = true;
|
||||
|
||||
public bool VoteRunning = false;
|
||||
|
||||
public enum VoteState { None = 0, Started = 1, Running = 2, Passed = 3, Failed = 4 };
|
||||
|
||||
private IReadOnlyDictionary<T, int> GetVoteCounts<T>(VoteType voteType, List<Client> voters)
|
||||
@@ -39,12 +31,12 @@ namespace Barotrauma
|
||||
|
||||
public T HighestVoted<T>(VoteType voteType, List<Client> voters)
|
||||
{
|
||||
if (voteType == VoteType.Sub && !AllowSubVoting) return default(T);
|
||||
if (voteType == VoteType.Mode && !AllowModeVoting) return default(T);
|
||||
if (voteType == VoteType.Sub && !GameMain.NetworkMember.ServerSettings.AllowSubVoting) { return default; }
|
||||
if (voteType == VoteType.Mode && !GameMain.NetworkMember.ServerSettings.AllowModeVoting) { return default; }
|
||||
|
||||
IReadOnlyDictionary<T, int> voteList = GetVoteCounts<T>(voteType, voters);
|
||||
|
||||
T selected = default(T);
|
||||
T selected = default;
|
||||
int highestVotes = 0;
|
||||
foreach (KeyValuePair<T, int> votable in voteList)
|
||||
{
|
||||
@@ -71,11 +63,13 @@ namespace Barotrauma
|
||||
{
|
||||
client.ResetVotes();
|
||||
}
|
||||
|
||||
GameMain.NetworkMember.EndVoteCount = 0;
|
||||
GameMain.NetworkMember.EndVoteMax = 0;
|
||||
|
||||
#if CLIENT
|
||||
foreach (VoteType voteType in Enum.GetValues(typeof(VoteType)))
|
||||
{
|
||||
SetVoteCountYes(voteType, 0);
|
||||
SetVoteCountNo(voteType, 0);
|
||||
SetVoteCountMax(voteType, 0);
|
||||
}
|
||||
UpdateVoteTexts(connectedClients, VoteType.Mode);
|
||||
UpdateVoteTexts(connectedClients, VoteType.Sub);
|
||||
#endif
|
||||
|
||||
@@ -348,6 +348,14 @@ namespace Barotrauma.Steam
|
||||
|
||||
string val = attribute.Value.CleanUpPathCrossPlatform(correctFilenameCase: false);
|
||||
|
||||
//Handle mods that have been mangled by pre-modding-refactor
|
||||
//copying of post-modding-refactor mods (what a clusterfuck)
|
||||
int modDirStrIndex = val.IndexOf(ContentPath.ModDirStr, StringComparison.OrdinalIgnoreCase);
|
||||
if (modDirStrIndex >= 0)
|
||||
{
|
||||
val = val[modDirStrIndex..];
|
||||
}
|
||||
|
||||
//Handle really old mods (0.9.0.4-era) that might be structured as
|
||||
//%ModDir%/Mods/[NAME]/[RESOURCE]
|
||||
string fullSrcPath = Path.Combine(fileListDir, val).CleanUpPath();
|
||||
@@ -418,7 +426,7 @@ namespace Barotrauma.Steam
|
||||
File.Copy(from, to, overwrite: true);
|
||||
}
|
||||
|
||||
private static async Task CopyDirectory(string fileListDir, string modName, string from, string to)
|
||||
public static async Task CopyDirectory(string fileListDir, string modName, string from, string to)
|
||||
{
|
||||
from = Path.GetFullPath(from); to = Path.GetFullPath(to);
|
||||
Directory.CreateDirectory(to);
|
||||
|
||||
@@ -79,10 +79,10 @@ namespace Barotrauma
|
||||
{
|
||||
roundData.EnteredCrushDepth.Add(c);
|
||||
}
|
||||
else if (Level.Loaded.GetRealWorldDepth(c.WorldPosition.Y) < Level.Loaded.RealWorldCrushDepth * 0.5f)
|
||||
else if (Level.Loaded.GetRealWorldDepth(c.WorldPosition.Y) < Level.Loaded.RealWorldCrushDepth - 500.0f)
|
||||
{
|
||||
//all characters that have entered crush depth and are still alive get an achievement
|
||||
if (roundData.EnteredCrushDepth.Contains(c)) UnlockAchievement(c, "survivecrushdepth".ToIdentifier());
|
||||
if (roundData.EnteredCrushDepth.Contains(c)) { UnlockAchievement(c, "survivecrushdepth".ToIdentifier()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,7 +426,7 @@ namespace Barotrauma
|
||||
!c.IsDead &&
|
||||
c.TeamID != CharacterTeamType.FriendlyNPC &&
|
||||
!(c.AIController is EnemyAIController) &&
|
||||
(c.Submarine == gameSession.Submarine || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
|
||||
(c.Submarine == gameSession.Submarine || gameSession.Submarine.GetConnectedSubs().Contains(c.Submarine) || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
|
||||
|
||||
if (charactersInSub.Count == 1)
|
||||
{
|
||||
@@ -454,6 +454,10 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (Character character in charactersInSub)
|
||||
{
|
||||
if (roundData.EnteredCrushDepth.Contains(character))
|
||||
{
|
||||
UnlockAchievement(character, "survivecrushdepth".ToIdentifier());
|
||||
}
|
||||
if (character.Info.Job == null) { continue; }
|
||||
UnlockAchievement(character, $"{character.Info.Job.Prefab.Identifier}round".ToIdentifier());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ namespace Barotrauma
|
||||
events.Remove(identifier);
|
||||
}
|
||||
|
||||
public void TryDeregister(Identifier identifier)
|
||||
{
|
||||
if (!HasEvent(identifier)) { return; }
|
||||
Deregister(identifier);
|
||||
}
|
||||
|
||||
public bool HasEvent(Identifier identifier) => events.ContainsKey(identifier);
|
||||
|
||||
public void Invoke(T data)
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Steamworks.Data;
|
||||
using Color = Microsoft.Xna.Framework.Color;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -227,7 +228,7 @@ namespace Barotrauma
|
||||
return Path.Combine(folder, saveName);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetSaveFiles(SaveType saveType, bool includeInCompatible = true)
|
||||
public static IReadOnlyList<CampaignMode.SaveInfo> GetSaveFiles(SaveType saveType, bool includeInCompatible = true)
|
||||
{
|
||||
string folder = saveType == SaveType.Singleplayer ? SaveFolder : MultiplayerSaveFolder;
|
||||
if (!Directory.Exists(folder))
|
||||
@@ -250,18 +251,61 @@ namespace Barotrauma
|
||||
files.AddRange(Directory.GetFiles(legacyFolder, "*.save", System.IO.SearchOption.TopDirectoryOnly));
|
||||
}
|
||||
|
||||
if (!includeInCompatible)
|
||||
List<CampaignMode.SaveInfo> saveInfos = new List<CampaignMode.SaveInfo>();
|
||||
foreach (string file in files)
|
||||
{
|
||||
for (int i = files.Count - 1; i >= 0; i--)
|
||||
XDocument doc = LoadGameSessionDoc(file);
|
||||
if (!includeInCompatible && !IsSaveFileCompatible(doc))
|
||||
{
|
||||
XDocument doc = LoadGameSessionDoc(files[i]);
|
||||
if (!IsSaveFileCompatible(doc))
|
||||
continue;
|
||||
}
|
||||
if (doc?.Root == null)
|
||||
{
|
||||
saveInfos.Add(new CampaignMode.SaveInfo()
|
||||
{
|
||||
files.RemoveAt(i);
|
||||
FilePath = file
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> enabledContentPackageNames = new List<string>();
|
||||
|
||||
//backwards compatibility
|
||||
string enabledContentPackagePathsStr = doc.Root.GetAttributeStringUnrestricted("selectedcontentpackages", string.Empty);
|
||||
foreach (string packagePath in enabledContentPackagePathsStr.Split('|'))
|
||||
{
|
||||
if (string.IsNullOrEmpty(packagePath)) { continue; }
|
||||
//change paths to names
|
||||
string fileName = Path.GetFileNameWithoutExtension(packagePath);
|
||||
if (fileName == "filelist")
|
||||
{
|
||||
enabledContentPackageNames.Add(Path.GetFileName(Path.GetDirectoryName(packagePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
enabledContentPackageNames.Add(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
string enabledContentPackageNamesStr = doc.Root.GetAttributeStringUnrestricted("selectedcontentpackagenames", string.Empty);
|
||||
//split on pipes, excluding pipes preceded by \
|
||||
foreach (string packageName in Regex.Split(enabledContentPackageNamesStr, @"(?<!(?<!\\)*\\)\|"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(packageName)) { continue; }
|
||||
enabledContentPackageNames.Add(packageName.Replace(@"\|", "|"));
|
||||
}
|
||||
|
||||
saveInfos.Add(new CampaignMode.SaveInfo()
|
||||
{
|
||||
FilePath = file,
|
||||
SubmarineName = doc?.Root?.GetAttributeStringUnrestricted("submarine", ""),
|
||||
SaveTime = doc.Root.GetAttributeInt("savetime", 0),
|
||||
EnabledContentPackageNames = enabledContentPackageNames.ToArray(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return files;
|
||||
|
||||
return saveInfos;
|
||||
}
|
||||
|
||||
public static string CreateSavePath(SaveType saveType, string fileName = "Save_Default")
|
||||
|
||||
@@ -1,3 +1,53 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.17.5.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Changes:
|
||||
- Changed server browser tooltips: list all missing mods instead of just missing ones that aren't available from the workshop.
|
||||
- Players without the permission to access the bank can request money from the bank (approved by vote).
|
||||
- Modified Selkie emergency hatch: can only use it if the shuttle is flooded.
|
||||
- Added limits to how many items a vending machine can dispense and adjusted what's available in them (unstable only).
|
||||
- Reworked campaign permissions: removed BuyItems and CampaignStore permissions (no longer needed, everyone can buy), added ManageMap and ManageHires permissions, ManageCampaign allows gives you all the other campaign-related permissions.
|
||||
- Adjusted the amount of colonies in cold caverns.
|
||||
- Added a prompt to allow modders to automatically transfer their mods and submarines from before v0.17 to the new LocalMods folder.
|
||||
- Disabled loading submarines from the Submarines folder, as it has been rendered obsolete by this change.
|
||||
- Removed submarine download confirmation prompt. All subs that are required to play in a server will be downloaded automatically, which shouldn't be a problem since they're only stored temporarily.
|
||||
- Swapped the sides of server log and character info in multiplayer tab menu.
|
||||
- Doubled the rate limit for medical clinic which should reduce "No response from server" errors.
|
||||
- Rebalance mudraptors: slightly more health, less damage at head.
|
||||
- Reduce the probability for the coilgun to dismember limbs (or break armor).
|
||||
- Crawler: adjust the vitality multipliers of hands and tail from 50% to 75%.
|
||||
|
||||
Fixes:
|
||||
- Fixed multiplayer campaign saves with semicolons or pipes in their name causing "path to a save file was empty" errors in the server lobby.
|
||||
- Fixed performance drops in multiplayer when someone attacks the outpost NPCs and causes the reputation to drop.
|
||||
- Moved the showperf view to the right to prevent it from overlapping with the crew list.
|
||||
- Fixed status monitor's electrical view and item finder not showing items in docked subs.
|
||||
- The sound of crowbaring a door open can't be heard from other subs.
|
||||
- Fixed "a gaze into the abyss" achievement working unreliably. The achievement didn't unlock until you returned to 50% of the crush depth, which isn't possible in some levels (e.g. if the level starts at 3500 m and crush depth at 5000 m). Now it's unlocked if you get to 500 m above the crush depth or to the end of the level.
|
||||
- Fixed certain achievements (last man standing, lone sailor, finishing a round with a specific job) not unlocking if you're in a docked sub instead of the main sub at the end of the round.
|
||||
- Readjusted all sitting animations so that they characters shouldn't twitch anymore while sitting.
|
||||
|
||||
Fixes (unstable only):
|
||||
- Fixed mods list being configurable outside of the main menu.
|
||||
- Misc fixes to colonies (added vents, wired docking ports, adjusted misaligned stairs, fixed a bunch of gap and draw order issues).
|
||||
- Fixed outpost generator sometimes placing a hallway between modules connected by stairs in colonies.
|
||||
- Fixed crashing when saving a sub.
|
||||
- Fixed cargo missions sometimes displaying an incorrect number of crates in the description.
|
||||
- Hide the "sufficient skills to fabricate"/"insufficient skills to fabricate" labels on the vending machines.
|
||||
- Decrease the number of daily specials and requested goods for merchants to account for the multiple vendors.
|
||||
- Fixed crash in Mission.GetSalaryEligibleCrew when taking control of a bot in the multiplayer campaign.
|
||||
- Fixed tab menu blocking all other UI elements.
|
||||
- Readjusted amount of materials in abyss islands.
|
||||
- Fixed wallet UI being accessible in all multiplayer game modes.
|
||||
- Fixed flashlights emitting sparks.
|
||||
- Flipped the confirm and reset buttons in wallet UI.
|
||||
- Added a new column to multiplayer tab menu that shows the amount of money the player has in their wallet.
|
||||
|
||||
Modding:
|
||||
- Option to make missions force a ruin in the level if there isn't one.
|
||||
- Character editor: don't check the validity of the texture path when copying humans (because the path is not valid and will be parsed later). Allows creating custom human characters by copying the vanilla human (even though they are not fully supported).
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.17.4.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user