Unstable 0.17.6.0

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