Build 0.18.4.0

This commit is contained in:
Markus Isberg
2022-05-31 23:13:05 +09:00
parent 077917fa5d
commit 64db1a6a44
175 changed files with 4916 additions and 2393 deletions
@@ -551,6 +551,7 @@ namespace Barotrauma
{
bool hasOwner = inc.ReadBoolean();
int ownerId = hasOwner ? inc.ReadByte() : -1;
float humanPrefabHealthMultiplier = inc.ReadSingle();
int balance = inc.ReadInt32();
int rewardDistribution = inc.ReadRangedInteger(0, 100);
byte teamID = inc.ReadByte();
@@ -573,6 +574,7 @@ namespace Barotrauma
{
character.MerchantIdentifier = inc.ReadIdentifier();
}
character.HumanPrefabHealthMultiplier = humanPrefabHealthMultiplier;
character.Wallet.Balance = balance;
character.Wallet.RewardDistribution = rewardDistribution;
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
@@ -6,7 +6,6 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -1345,6 +1344,7 @@ namespace Barotrauma
{
UserData = item,
DisabledColor = Color.White * 0.1f,
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (!(userdata is ItemPrefab itemPrefab)) { return false; }
@@ -1352,6 +1352,7 @@ namespace Barotrauma
if (item == null) { return false; }
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == selectedLimbIndex);
item.ApplyTreatment(Character.Controlled, Character, targetLimb);
SoundPlayer.PlayUISound(GUISoundType.Select);
return true;
}
};
@@ -108,6 +108,15 @@ namespace Barotrauma
}
}
public void RemoveFile(File file)
{
if (HasFile(file))
{
files.Remove(file);
DiscardHashAndInstallTime();
}
}
public void DiscardHashAndInstallTime()
{
ExpectedHash = null;
@@ -144,7 +153,7 @@ namespace Barotrauma
=> rootElement.Add(new XAttribute(name, value.ToString() ?? ""));
addRootAttribute("name", Name);
addRootAttribute("modversion", ModVersion);
if (!ModVersion.IsNullOrEmpty()) { addRootAttribute("modversion", ModVersion); }
addRootAttribute("corepackage", IsCore);
if (SteamWorkshopId != 0) { addRootAttribute("steamworkshopid", SteamWorkshopId); }
addRootAttribute("gameversion", GameMain.Version);
@@ -0,0 +1,133 @@
#nullable enable
using System;
using System.Linq;
using Barotrauma.Steam;
using Barotrauma.IO;
namespace Barotrauma
{
public static class ModMerger
{
public static void AskMerge(ContentPackage[] mods)
{
ErrorIfNonLocal(mods);
var msgBox = new GUIMessageBox(TextManager.Get("MergeModsHeader"), "", relativeSize: (0.5f, 0.8f),
buttons: new LocalizedString[] { TextManager.Get("ConfirmModMerge"), TextManager.Get("Cancel") });
msgBox.Buttons[1].OnClicked = msgBox.Close;
var desc = new GUITextBlock(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform), TextManager.Get("MergeModsDesc"));
var modsList = new GUIListBox(new RectTransform((1.0f, 0.5f), msgBox.Content.RectTransform))
{
OnSelected = (component, o) => false,
HoverCursor = CursorState.Default
};
foreach (var mod in mods)
{
new GUITextBlock(new RectTransform((1.0f, 0.11f), modsList.Content.RectTransform), mod.Name)
{
CanBeFocused = false
};
}
var footer = new GUITextBlock(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform), TextManager.Get("MergeModsFooter"));
var resultName = new GUITextBox(new RectTransform((1.0f, 0.1f), msgBox.Content.RectTransform))
{
Text = (mods.Count(m => m.Files.Length > 1)==1)
? mods.First(m => m.Files.Length > 1).Name
: ""
};
void flashText()
{
resultName!.Select();
resultName.Flash(GUIStyle.Red);
}
msgBox.Buttons[0].OnClicked = (button, o) =>
{
if (string.IsNullOrEmpty(resultName.Text))
{
flashText();
return false;
}
string targetDir = $"{ContentPackage.LocalModsDir}/{resultName.Text}";
bool dirMatches(ContentPackage mod)
=> mod.Dir.CleanUpPathCrossPlatform(correctFilenameCase: false)
.Equals(targetDir, StringComparison.OrdinalIgnoreCase);
if (ContentPackageManager.LocalPackages.Any(dirMatches)
&& !mods.Any(dirMatches))
{
flashText();
return false;
}
MergeMods(mods, resultName.Text);
msgBox.Close();
return false;
};
}
private static void MergeMods(ContentPackage[] mods, string resultName)
{
ModProject resultProject = new ModProject
{
Name = resultName
};
string targetDir = $"{ContentPackage.LocalModsDir}/{resultName}";
Directory.CreateDirectory(targetDir);
foreach (var mod in mods)
{
foreach (var file in Directory.GetFiles(mod.Dir, "*", System.IO.SearchOption.AllDirectories)
.Select(f => f.CleanUpPathCrossPlatform(correctFilenameCase: false)))
{
if (Path.GetFileName(file).Equals(ContentPackage.FileListFileName, StringComparison.OrdinalIgnoreCase)) { continue; }
string targetFilePath = file[mod.Dir.Length..];
if (targetFilePath.StartsWith("/") || targetFilePath.StartsWith("\\"))
{
targetFilePath = targetFilePath[1..];
}
targetFilePath = Path.Combine(targetDir, targetFilePath).CleanUpPathCrossPlatform(correctFilenameCase: false);
//DebugConsole.NewMessage(targetFilePath);
Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath)!);
File.Copy(file, targetFilePath, overwrite: true);
var oldFileInProject = resultProject.Files.FirstOrDefault(f
=> f.Path.Equals(targetFilePath, StringComparison.OrdinalIgnoreCase));
if (oldFileInProject != null)
{
resultProject.RemoveFile(oldFileInProject);
}
var fileInMod = mod.Files.Find(f => f.Path == file);
if (fileInMod != null)
{
var newFileInProject = ModProject.File.FromPath(targetFilePath, fileInMod.GetType());
resultProject.AddFile(newFileInProject);
}
}
}
resultProject.Save(Path.Combine(targetDir, ContentPackage.FileListFileName));
foreach (var mod in mods)
{
Directory.Delete(mod.Dir);
}
(SettingsMenu.Instance!.WorkshopMenu as MutableWorkshopMenu)!.PopulateInstalledModLists(forceRefreshEnabled: true, refreshDisabled: true);
}
private static void ErrorIfNonLocal(ContentPackage[] mods)
{
var nonLocal = mods.Where(m => !ContentPackageManager.LocalPackages.Contains(m)).ToArray();
if (nonLocal.Any())
{
throw new Exception($"{string.Join(", ", nonLocal.Select(m => m.Name))} are not local mods");
}
}
}
}
@@ -1,7 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -9,9 +8,7 @@ using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using Microsoft.Xna.Framework;
using Directory = Barotrauma.IO.Directory;
using File = Barotrauma.IO.File;
using Path = Barotrauma.IO.Path;
using Barotrauma.IO;
namespace Barotrauma.Transition
{
@@ -258,13 +255,13 @@ namespace Barotrauma.Transition
{
string[] getFiles(string path, string pattern)
=> Directory.Exists(path)
? Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly)
? Directory.GetFiles(path, pattern, System.IO.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, "*", System.IO.SearchOption.TopDirectoryOnly);
var publishedItems = await SteamManager.Workshop.GetPublishedItems();
foreach (var modDir in allOldMods)
@@ -107,6 +107,7 @@ namespace Barotrauma
var buttonLeft = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -150,6 +151,7 @@ namespace Barotrauma
var buttonRight = new GUIButton(new RectTransform(new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style: "DeviceButton")
{
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -178,6 +180,7 @@ namespace Barotrauma
TextColor = new Color(51, 59, 46),
SelectedTextColor = GUIStyle.Green,
UserData = i,
PlaySoundOnSelect = false,
OnClicked = (btn, userdata) =>
{
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
@@ -357,10 +360,15 @@ namespace Barotrauma
CanBeFocused = true,
ForceUpperCase = ForceUpperCase.No,
UserData = message.SenderClient,
PlaySoundOnSelect = false,
OnClicked = (_, o) =>
{
if (!(o is Client client)) { return false; }
GameMain.NetLobbyScreen?.SelectPlayer(client);
if (GameMain.NetLobbyScreen != null)
{
GameMain.NetLobbyScreen.SelectPlayer(client);
SoundPlayer.PlayUISound(GUISoundType.Select);
}
return true;
},
OnSecondaryClicked = (_, o) =>
@@ -178,7 +178,14 @@ namespace Barotrauma
return Sprites.ContainsKey(state) ? Sprites[state]?.First()?.Sprite : null;
}
public void GetSize(XElement element)
public void RefreshSize()
{
Width = null;
Height = null;
GetSize(Element);
}
private void GetSize(XElement element)
{
Point size = new Point(0, 0);
foreach (var subElement in element.Elements())
@@ -193,12 +193,13 @@ namespace Barotrauma
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ClickSound = GUISoundType.HireRepairClick,
ClickSound = GUISoundType.ConfirmTransaction,
ForceUpperCase = ForceUpperCase.Yes,
OnClicked = (b, o) => ValidateHires(PendingHires, true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.Cart,
ForceUpperCase = ForceUpperCase.Yes,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
@@ -403,6 +404,7 @@ namespace Barotrauma
{
var hireButton = new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
{
ClickSound = GUISoundType.Cart,
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
@@ -429,6 +431,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
{
ClickSound = GUISoundType.Cart,
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
@@ -182,7 +182,10 @@ namespace Barotrauma
window = new GUIFrame(new RectTransform(Vector2.One * 0.8f, backgroundFrame.RectTransform, Anchor.Center));
var horizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, window.RectTransform, Anchor.Center), true);
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform));
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform))
{
PlaySoundOnSelect = true
};
var drives = System.IO.DriveInfo.GetDrives();
foreach (var drive in drives)
@@ -241,6 +244,7 @@ namespace Barotrauma
fileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), fileListLayout.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (child, userdata) =>
{
if (userdata is null) { return false; }
@@ -24,15 +24,17 @@ namespace Barotrauma
ChatMessage,
RadioMessage,
DeadMessage,
Click,
Select,
PickItem,
PickItemFail,
DropItem,
PopupMenu,
DecreaseQuantity,
IncreaseQuantity,
HireRepairClick,
UISwitch
Decrease,
Increase,
UISwitch,
TickBox,
ConfirmTransaction,
Cart,
}
public enum CursorState
@@ -2384,7 +2386,7 @@ namespace Barotrauma
CreateButton("PauseMenuResume", buttonContainer, null);
CreateButton("PauseMenuSettings", buttonContainer, () => SettingsMenuOpen = true);
bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost;
bool IsFriendlyOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedFriendlyOutpost;
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
{
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
@@ -2399,11 +2401,11 @@ namespace Barotrauma
GameMain.GameSession.LoadPreviousSave();
});
if (IsOutpostLevel())
if (IsFriendlyOutpostLevel())
{
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToMainMenuVerification", action: () =>
{
if (IsOutpostLevel()) { GameMain.QuitToMainMenu(save: true); }
if (IsFriendlyOutpostLevel()) { GameMain.QuitToMainMenu(save: true); }
});
}
}
@@ -2416,7 +2418,7 @@ namespace Barotrauma
}
else if (!GameMain.GameSession.GameMode.IsSinglePlayer && GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ManageRound))
{
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsOutpostLevel();
bool canSave = GameMain.GameSession.GameMode is CampaignMode && IsFriendlyOutpostLevel();
if (canSave)
{
CreateButton("PauseMenuSaveQuit", buttonContainer, verificationTextTag: "PauseMenuSaveAndReturnToServerLobbyVerification", action: () =>
@@ -159,7 +159,9 @@ namespace Barotrauma
private float pulseExpand;
private bool flashed;
public GUISoundType ClickSound { get; set; } = GUISoundType.Click;
public GUISoundType ClickSound { get; set; } = GUISoundType.Select;
public override bool PlaySoundOnSelect { get; set; } = true;
public GUIButton(RectTransform rectT, Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : this(rectT, new RawLString(""), textAlignment, style, color) { }
@@ -247,7 +249,10 @@ namespace Barotrauma
}
else if (PlayerInput.PrimaryMouseButtonClicked())
{
SoundPlayer.PlayUISound(ClickSound);
if (PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(ClickSound);
}
if (OnClicked != null)
{
if (OnClicked(this, UserData))
@@ -383,6 +383,8 @@ namespace Barotrauma
public bool ExternalHighlight = false;
public virtual bool PlaySoundOnSelect { get; set; } = false;
private RectTransform rectTransform;
public RectTransform RectTransform
{
@@ -113,7 +113,8 @@ namespace Barotrauma
{
AutoHideScrollBar = false,
ScrollBarVisible = false,
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding
Padding = hasHeader ? new Vector4(4, 0, 4, 4) : padding,
PlaySoundOnSelect = true
};
foreach (var (option, size) in optionsAndSizes)
@@ -183,7 +183,8 @@ namespace Barotrauma
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
{ IsFixedSize = false }, style: null)
{
Enabled = !selectMultiple
Enabled = !selectMultiple,
PlaySoundOnSelect = true,
};
if (!selectMultiple) { listBox.OnSelected = SelectItem; }
GUIStyle.Apply(listBox, "GUIListBox", this);
@@ -309,6 +309,45 @@ namespace Barotrauma
}
}
public override bool PlaySoundOnSelect { get; set; } = false;
public bool PlaySoundOnDragStop { get; set; } = false;
public GUISoundType? SoundOnDragStart { get; set; } = null;
public GUISoundType? SoundOnDragStop { get; set; } = null;
#region enums
public enum Force
{
Yes,
No
}
public enum AutoScroll
{
Enabled,
Disabled
}
public enum TakeKeyBoardFocus
{
Yes,
No
}
public enum PlaySelectSound
{
Yes,
No
}
private AutoScroll GetAutoScroll(bool b)
{
return b ? AutoScroll.Enabled : AutoScroll.Disabled;
}
#endregion
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{
@@ -396,7 +435,7 @@ namespace Barotrauma
UpdateScrollBarSize();
}
public void Select(object userData, bool force = false, bool autoScroll = true)
public void Select(object userData, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled)
{
var children = Content.Children;
int i = 0;
@@ -515,9 +554,12 @@ namespace Barotrauma
/// Scrolls the list to the specific element.
/// </summary>
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component, bool playSound = true)
public void ScrollToElement(GUIComponent component, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.Click); }
if (playSelectSound == PlaySelectSound.Yes)
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
@@ -573,9 +615,16 @@ namespace Barotrauma
}
}
private double lastDragStartTime;
private void StartDraggingElement(GUIComponent child)
{
DraggedElement = child;
if (Timing.TotalTime > lastDragStartTime + 0.2f)
{
lastDragStartTime = Timing.TotalTime;
SoundPlayer.PlayUISound(SoundOnDragStart);
}
}
private bool UpdateDragging()
@@ -586,6 +635,10 @@ namespace Barotrauma
var draggedElem = draggedElement;
OnRearranged?.Invoke(this, draggedElem.UserData);
DraggedElement = null;
if (PlaySoundOnDragStop)
{
SoundPlayer.PlayUISound(SoundOnDragStop);
}
RepositionChildren();
if (AllSelected.Contains(draggedElem)) { return true; }
}
@@ -710,7 +763,7 @@ namespace Barotrauma
int index = Content.Children.ToList().IndexOf(component);
if (index >= 0)
{
Select(index, false, false, takeKeyBoardFocus: true);
Select(index, autoScroll: AutoScroll.Disabled, takeKeyBoardFocus: TakeKeyBoardFocus.Yes);
}
}
}
@@ -733,7 +786,7 @@ namespace Barotrauma
{
ScrollToElement(child);
}
Select(i, autoScroll: false, takeKeyBoardFocus: true);
Select(i, autoScroll: AutoScroll.Disabled, takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
if (CurrentDragMode != DragMode.NoDragging
@@ -929,14 +982,13 @@ namespace Barotrauma
if (ClampScrollToElements)
{
bool scrollDown = Math.Clamp(PlayerInput.ScrollWheelSpeed, 0, 1) > 0;
if (scrollDown)
{
SelectPrevious(takeKeyBoardFocus: true);
SelectPrevious(takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
else
{
SelectNext(takeKeyBoardFocus: true);
SelectNext(takeKeyBoardFocus: TakeKeyBoardFocus.Yes, playSelectSound: PlaySelectSound.Yes);
}
}
}
@@ -964,7 +1016,7 @@ namespace Barotrauma
return FindScrollableParentListBox(target.Parent);
}
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void SelectNext(Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
int index = SelectedIndex + 1;
while (index < Content.CountChildren)
@@ -972,10 +1024,10 @@ namespace Barotrauma
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
if (SmoothScroll)
{
ScrollToElement(child);
ScrollToElement(child, playSelectSound);
}
break;
}
@@ -983,7 +1035,7 @@ namespace Barotrauma
}
}
public void SelectPrevious(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void SelectPrevious(Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
int index = SelectedIndex - 1;
while (index >= 0)
@@ -991,10 +1043,10 @@ namespace Barotrauma
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
Select(index, force, GetAutoScroll(!SmoothScroll && autoScroll == AutoScroll.Enabled), takeKeyBoardFocus, playSelectSound);
if (SmoothScroll)
{
ScrollToElement(child);
ScrollToElement(child, playSelectSound);
}
break;
}
@@ -1002,7 +1054,7 @@ namespace Barotrauma
}
}
public void Select(int childIndex, bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
public void Select(int childIndex, Force force = Force.No, AutoScroll autoScroll = AutoScroll.Enabled, TakeKeyBoardFocus takeKeyBoardFocus = TakeKeyBoardFocus.No, PlaySelectSound playSelectSound = PlaySelectSound.No)
{
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
@@ -1013,7 +1065,7 @@ namespace Barotrauma
if (OnSelected != null)
{
// TODO: The callback is called twice, fix this!
wasSelected = force || OnSelected(child, child.UserData);
wasSelected = force == Force.Yes || OnSelected(child, child.UserData);
}
if (!wasSelected) { return; }
@@ -1055,7 +1107,7 @@ namespace Barotrauma
// Ensure that the selected element is visible. This may not be the case, if the selection is run from code. (e.g. if we have two list boxes that are synced)
// TODO: This method only works when moving one item up/down (e.g. when using the up and down arrows)
if (autoScroll)
if (autoScroll == AutoScroll.Enabled)
{
if (ScrollBar.IsHorizontal)
{
@@ -1086,11 +1138,19 @@ namespace Barotrauma
}
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
if (takeKeyBoardFocus && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
if (takeKeyBoardFocus == TakeKeyBoardFocus.Yes && CanTakeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
{
Selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
}
// List box child components can be parents to other components that can play sounds when selected (e.g. store elements)
// so the list box shouldn't play the Select sound if the GUI.MouseOn component has a sound to play
if (playSelectSound == PlaySelectSound.Yes && PlaySoundOnSelect && !child.PlaySoundOnSelect &&
(GUI.MouseOn == null || GUI.MouseOn.Parent == Content || !GUI.MouseOn.PlaySoundOnSelect))
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
}
public void Select(IEnumerable<GUIComponent> children)
@@ -1293,16 +1353,16 @@ namespace Barotrauma
switch (key)
{
case Keys.Down:
if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
if (!isHorizontal && AllowArrowKeyScroll) { SelectNext(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Up:
if (!isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
if (!isHorizontal && AllowArrowKeyScroll) { SelectPrevious(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Left:
if (isHorizontal && AllowArrowKeyScroll) { SelectPrevious(); }
if (isHorizontal && AllowArrowKeyScroll) { SelectPrevious(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Right:
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(); }
if (isHorizontal && AllowArrowKeyScroll) { SelectNext(playSelectSound: PlaySelectSound.Yes); }
break;
case Keys.Enter:
case Keys.Space:
@@ -182,7 +182,7 @@ namespace Barotrauma
public float valueStep;
private float pressedTimer;
private float pressedDelay = 0.5f;
private readonly float pressedDelay = 0.5f;
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null, bool hidePlusMinusButtons = false) : base(style, rectT)
@@ -228,6 +228,7 @@ namespace Barotrauma
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
PlusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
GUIStyle.Apply(PlusButton, "PlusButton", this);
PlusButton.ClickSound = GUISoundType.Increase;
PlusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
@@ -249,6 +250,7 @@ namespace Barotrauma
MinusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
GUIStyle.Apply(MinusButton, "MinusButton", this);
MinusButton.ClickSound = GUISoundType.Decrease;
MinusButton.OnButtonDown += () =>
{
pressedTimer = pressedDelay;
@@ -423,8 +425,8 @@ namespace Barotrauma
intValue = Math.Min(intValue, MaxValueInt.Value);
UpdateText();
}
PlusButton.Enabled = intValue < MaxValueInt;
MinusButton.Enabled = intValue > MinValueInt;
PlusButton.Enabled = MaxValueInt == null || intValue < MaxValueInt;
MinusButton.Enabled = MinValueInt == null || intValue > MinValueInt;
}
private void UpdateText()
@@ -98,7 +98,6 @@ namespace Barotrauma
foreach (var subElement in element.Elements().Reverse())
{
if (subElement.NameAsIdentifier() != "override") { continue; }
if (subElement.GetAttributeBool("iscjk", false))
{
return new ScalableFont(subElement, GameMain.Instance.GraphicsDevice);
@@ -111,8 +110,7 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeContentPath("file")?.Value;
}
@@ -125,8 +123,7 @@ namespace Barotrauma
//check if any of the language override fonts want to override the font size as well
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.CurrentConfig.Graphics.TextScale); }
@@ -149,8 +146,7 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeBool("dynamicloading", false);
}
@@ -162,14 +158,20 @@ namespace Barotrauma
{
foreach (var subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
if (GameSettings.CurrentConfig.Language == subElement.GetAttributeIdentifier("language", "").ToLanguageIdentifier())
if (IsValidOverride(subElement))
{
return subElement.GetAttributeBool("iscjk", false);
}
}
return element.GetAttributeBool("iscjk", false);
}
private bool IsValidOverride(XElement element)
{
if (!element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { return false; }
var languages = element.GetAttributeIdentifierArray("language", Array.Empty<Identifier>());
return languages.Any(l => l.ToLanguageIdentifier() == GameSettings.CurrentConfig.Language);
}
}
public class GUIFont : GUISelector<GUIFontPrefab>
@@ -322,9 +322,8 @@ namespace Barotrauma
{
if (!enabled || !PlayerInput.PrimaryMouseButtonDown()) { return false; }
if (barSize >= 1.0f) { return false; }
DraggingBar = this;
SoundPlayer.PlayUISound(GUISoundType.Select);
return true;
}
@@ -34,7 +34,6 @@ namespace Barotrauma
public readonly static PrefabCollection<GUIComponentStyle> ComponentStyles = new PrefabCollection<GUIComponentStyle>();
public readonly static GUIFont Font = new GUIFont("Font");
public readonly static GUIFont GlobalFont = new GUIFont("GlobalFont");
public readonly static GUIFont UnscaledSmallFont = new GUIFont("UnscaledSmallFont");
public readonly static GUIFont SmallFont = new GUIFont("SmallFont");
public readonly static GUIFont LargeFont = new GUIFont("LargeFont");
@@ -142,10 +141,6 @@ namespace Barotrauma
public readonly static GUIColor HealthBarColorMedium = new GUIColor("HealthBarColorMedium");
public readonly static GUIColor HealthBarColorHigh = new GUIColor("HealthBarColorHigh");
public readonly static GUIColor EquipmentIndicatorNotEquipped = new GUIColor("EquipmentIndicatorNotEquipped");
public readonly static GUIColor EquipmentIndicatorEquipped = new GUIColor("EquipmentIndicatorEquipped");
public readonly static GUIColor EquipmentIndicatorRunningOut = new GUIColor("EquipmentIndicatorRunningOut");
public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
@@ -159,7 +154,7 @@ namespace Barotrauma
public static void Apply(GUIComponent targetComponent, Identifier styleName, GUIComponent parent = null)
{
GUIComponentStyle componentStyle = null;
GUIComponentStyle componentStyle;
if (parent != null)
{
GUIComponentStyle parentStyle = parent.Style;
@@ -251,6 +251,8 @@ namespace Barotrauma
public bool Readonly { get; set; }
public override bool PlaySoundOnSelect { get; set; } = true;
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, GUIFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT)
@@ -363,6 +365,10 @@ namespace Barotrauma
selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
OnSelected?.Invoke(this, Keys.None);
if (PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(GUISoundType.Select);
}
}
public void Deselect()
@@ -1,15 +1,13 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
public class GUITickBox : GUIComponent
{
private GUILayoutGroup layoutGroup;
private GUIFrame box;
private GUITextBlock text;
private readonly GUILayoutGroup layoutGroup;
private readonly GUIFrame box;
private readonly GUITextBlock text;
public delegate bool OnSelectedHandler(GUITickBox obj);
public OnSelectedHandler OnSelected;
@@ -129,6 +127,12 @@ namespace Barotrauma
set { text.Text = value; }
}
public float ContentWidth { get; private set; }
public GUISoundType SoundType { private get; set; } = GUISoundType.TickBox;
public override bool PlaySoundOnSelect { get; set; } = true;
public GUITickBox(RectTransform rectT, LocalizedString label, GUIFont font = null, string style = "") : base(null, rectT)
{
CanBeFocused = true;
@@ -180,6 +184,7 @@ namespace Barotrauma
box.RectTransform.MinSize = new Point(Rect.Height);
box.RectTransform.Resize(box.RectTransform.MinSize);
text.SetTextPos();
ContentWidth = box.Rect.Width + text.Padding.X + text.TextSize.X + text.Padding.Z;
}
protected override void Update(float deltaTime)
@@ -209,6 +214,10 @@ namespace Barotrauma
{
Selected = true;
}
if (PlaySoundOnSelect)
{
SoundPlayer.PlayUISound(SoundType);
}
}
}
else if (isSelected)
@@ -122,7 +122,7 @@ namespace Barotrauma
//horizontal slices at the corners of the screen for health bar and affliction icons
int afflictionAreaHeight = (int)(50 * GUI.Scale);
int healthBarWidth = (int)(BottomRightInfoArea.Width * 1.58f);
int healthBarWidth = (int)(BottomRightInfoArea.Width * 1.3f);
int healthBarHeight = (int)(50f * GUI.Scale);
HealthBarArea = new Rectangle(BottomRightInfoArea.Right - healthBarWidth + (int)Math.Floor(1 / GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + GUI.IntScale(10), healthBarWidth, healthBarHeight);
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
@@ -569,6 +569,7 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal"))
{
ClickSound = GUISoundType.ConfirmTransaction,
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetBalance() >= medicalClinic.GetTotalCost(),
OnClicked = (button, _) =>
{
@@ -595,6 +596,7 @@ namespace Barotrauma
GUIButton clearButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.Cart,
OnClicked = (button, _) =>
{
button.Enabled = false;
@@ -684,6 +686,7 @@ namespace Barotrauma
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), style: "CrewManagementRemoveButton")
{
ClickSound = GUISoundType.Cart,
OnClicked = (button, _) =>
{
button.Enabled = false;
@@ -766,6 +769,7 @@ namespace Barotrauma
GUIButton treatAllButton = new GUIButton(new RectTransform(new Vector2(1f, 0.2f), mainLayout.RectTransform), TextManager.Get("medicalclinic.treatall"))
{
ClickSound = GUISoundType.Cart,
Font = GUIStyle.SubHeadingFont,
Visible = false
};
@@ -887,7 +891,10 @@ namespace Barotrauma
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.25f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.SubHeadingFont);
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton");
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton")
{
ClickSound = GUISoundType.Cart
};
ImmutableArray<GUIComponent> elementsToDisable = ImmutableArray.Create<GUIComponent>(prefabBlock, backgroundFrame, icon, vitalityBlock, severityBlock, buyButton, descriptionBlock, priceBlock);
@@ -390,7 +390,7 @@ namespace Barotrauma
ToolTip = TextManager.Get("campaignstore.reputationtooltip")
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), reputationEffectContainer.RectTransform),
TextManager.Get("reputation"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
TextManager.Get("reputationmodifier"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
{
AutoScaleVertical = true,
CanBeFocused = false,
@@ -656,7 +656,7 @@ namespace Barotrauma
SetConfirmButtonBehavior();
clearAllButton = new GUIButton(new RectTransform(new Vector2(0.35f, 1.0f), buttonContainer.RectTransform), TextManager.Get("campaignstore.clearall"))
{
ClickSound = GUISoundType.DecreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = HasActiveTabPermissions(),
ForceUpperCase = ForceUpperCase.Yes,
OnClicked = (button, userData) =>
@@ -1567,8 +1567,6 @@ namespace Barotrauma
}
AddToShoppingCrate(purchasedItem, quantity: numberInput.IntValue - purchasedItem.Quantity);
};
amountInput.PlusButton.ClickSound = GUISoundType.IncreaseQuantity;
amountInput.MinusButton.ClickSound = GUISoundType.DecreaseQuantity;
frame.HoverColor = frame.SelectedColor = Color.Transparent;
}
@@ -1622,7 +1620,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton")
{
ClickSound = GUISoundType.IncreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = !forceDisable && pi.Quantity > 0,
ForceUpperCase = ForceUpperCase.Yes,
UserData = "addbutton",
@@ -1633,7 +1631,7 @@ namespace Barotrauma
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreRemoveFromCrateButton")
{
ClickSound = GUISoundType.DecreaseQuantity,
ClickSound = GUISoundType.Cart,
Enabled = !forceDisable,
ForceUpperCase = ForceUpperCase.Yes,
UserData = "removebutton",
@@ -2076,11 +2074,13 @@ namespace Barotrauma
{
if (IsBuying)
{
confirmButton.ClickSound = GUISoundType.ConfirmTransaction;
confirmButton.Text = TextManager.Get("CampaignStore.Purchase");
confirmButton.OnClicked = (b, o) => BuyItems();
}
else
{
confirmButton.ClickSound = GUISoundType.Select;
confirmButton.Text = TextManager.Get("CampaignStoreTab.Sell");
confirmButton.OnClicked = (b, o) =>
{
@@ -2088,6 +2088,7 @@ namespace Barotrauma
TextManager.Get("FireWarningHeader"),
TextManager.Get("CampaignStore.SellWarningText"),
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
confirmDialog.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
confirmDialog.Buttons[0].OnClicked = (b, o) => SellItems();
confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
confirmDialog.Buttons[1].OnClicked = confirmDialog.Close;
@@ -29,6 +29,8 @@ namespace Barotrauma
private GUITextBlock descriptionTextBlock;
private int selectionIndicatorThickness;
private GUIImage listBackground;
private GUITickBox transferItemsTickBox;
private GUITextBlock itemTransferReminderBlock;
private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
@@ -61,6 +63,23 @@ namespace Barotrauma
public GUIButton previewButton;
}
private bool TransferItemsOnSwitch
{
get
{
return transferItemsOnSwitch;
}
set
{
transferItemsOnSwitch = value;
if (transferItemsTickBox != null)
{
transferItemsTickBox.Selected = value;
}
}
}
private bool transferItemsOnSwitch = true;
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
{
if (GameMain.GameSession.Campaign == null) { return; }
@@ -149,11 +168,12 @@ namespace Barotrauma
GUIListBox descriptionFrame = new GUIListBox(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform), style: null) { Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding / 2f) };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUIStyle.Font, wrap: true) { CanBeFocused = false };
GUILayoutGroup buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), content.RectTransform), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
GUILayoutGroup bottomContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.075f), content.RectTransform, Anchor.CenterRight), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
float transferInfoFrameWidth = 1.0f;
if (closeAction != null)
{
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
{
OnClicked = (button, userData) =>
{
@@ -161,11 +181,33 @@ namespace Barotrauma
return true;
}
};
transferInfoFrameWidth -= closeButton.RectTransform.RelativeSize.X;
}
if (purchaseService) confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
if (purchaseService)
{
confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
transferInfoFrameWidth -= confirmButtonAlt.RectTransform.RelativeSize.X;
}
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), bottomContainer.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
SetConfirmButtonState(false);
transferInfoFrameWidth -= confirmButton.RectTransform.RelativeSize.X;
GUIFrame transferInfoFrame = new GUIFrame(new RectTransform(new Vector2(transferInfoFrameWidth, 1.0f), bottomContainer.RectTransform), style: null)
{
CanBeFocused = false
};
transferItemsTickBox = new GUITickBox(new RectTransform(new Vector2(0.2f, 1.0f), transferInfoFrame.RectTransform, Anchor.CenterRight), TextManager.Get("transferitems"), font: GUIStyle.SubHeadingFont)
{
Selected = TransferItemsOnSwitch,
Visible = false,
OnSelected = (tb) => transferItemsOnSwitch = tb.Selected
};
transferItemsTickBox.RectTransform.Resize(new Point(Math.Min((int)transferItemsTickBox.ContentWidth, transferInfoFrame.Rect.Width), transferItemsTickBox.Rect.Height));
itemTransferReminderBlock = new GUITextBlock(new RectTransform(Vector2.One, transferInfoFrame.RectTransform, Anchor.CenterRight), null)
{
TextAlignment = Alignment.CenterRight,
Visible = false
};
pageIndicatorHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.5f), submarineControlsGroup.RectTransform), style: null);
pageIndicator = GUIStyle.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
@@ -272,7 +314,7 @@ namespace Barotrauma
}
}
public void RefreshSubmarineDisplay(bool updateSubs)
public void RefreshSubmarineDisplay(bool updateSubs, bool setTransferOptionToTrue = false)
{
if (!initialized)
{
@@ -286,6 +328,10 @@ namespace Barotrauma
{
playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
}
if (setTransferOptionToTrue)
{
TransferItemsOnSwitch = true;
}
if (updateSubs)
{
UpdateSubmarines();
@@ -401,6 +447,10 @@ namespace Barotrauma
{
SelectSubmarine(null, Rectangle.Empty);
}
else
{
UpdateItemTransferInfoFrame();
}
}
private void UpdateSubmarines()
@@ -553,6 +603,40 @@ namespace Barotrauma
selectedSubmarineIndicator.RectTransform.NonScaledSize = Point.Zero;
SetConfirmButtonState(false);
}
UpdateItemTransferInfoFrame();
}
private void UpdateItemTransferInfoFrame()
{
if (selectedSubmarine != null)
{
var pendingSub = GameMain.GameSession?.Campaign?.PendingSubmarineSwitch;
if (Submarine.MainSub?.Info?.Name == selectedSubmarine.Name && pendingSub == null)
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Visible = false;
}
else if (pendingSub?.Name == selectedSubmarine.Name)
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Text = GameMain.GameSession.Campaign.TransferItemsOnSubSwitch ?
TextManager.Get("itemtransferenabledreminder") :
TextManager.Get("itemtransferdisabledreminder");
itemTransferReminderBlock.Visible = true;
}
else
{
transferItemsTickBox.Selected = TransferItemsOnSwitch;
transferItemsTickBox.Visible = true;
itemTransferReminderBlock.Visible = false;
}
}
else
{
transferItemsTickBox.Visible = false;
itemTransferReminderBlock.Visible = false;
}
}
private void SetConfirmButtonState(bool state)
@@ -614,24 +698,27 @@ namespace Barotrauma
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName),
("[amount]", deliveryFee.ToString()),
("[currencyname]", currencyName)), messageBoxOptions);
msgBox.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext",
var text = TextManager.GetWithVariables("switchsubmarinetext",
("[submarinename1]", CurrentOrPendingSubmarine().DisplayName),
("[submarinename2]", selectedSubmarine.DisplayName)), messageBoxOptions);
("[submarinename2]", selectedSubmarine.DisplayName));
text += GetItemTransferText();
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), text, messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, deliveryFee);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.SwitchSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.SwitchSub);
}
return true;
};
@@ -653,23 +740,25 @@ namespace Barotrauma
if (!purchaseOnly)
{
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
var text = TextManager.GetWithVariables("purchaseandswitchsubmarinetext",
("[submarinename1]", selectedSubmarine.DisplayName),
("[amount]", selectedSubmarine.Price.ToString()),
("[currencyname]", currencyName),
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName)), messageBoxOptions);
("[submarinename2]", CurrentOrPendingSubmarine().DisplayName));
text += GetItemTransferText();
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), text, messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, TransferItemsOnSwitch, 0);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseAndSwitchSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, TransferItemsOnSwitch, Networking.VoteType.PurchaseAndSwitchSub);
}
return true;
};
@@ -690,14 +779,20 @@ namespace Barotrauma
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseSub);
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, false, Networking.VoteType.PurchaseSub);
}
return true;
};
}
msgBox.Buttons[0].ClickSound = GUISoundType.ConfirmTransaction;
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
}
private LocalizedString GetItemTransferText()
{
return "\n\n" + TextManager.Get(TransferItemsOnSwitch ? "itemswillbetransferred" : "itemswontbetransferred");
}
}
}
@@ -360,7 +360,7 @@ namespace Barotrauma
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
talentsButton.OnAddedToGUIUpdateList += (component) =>
{
talentsButton.Enabled = Character.Controlled?.Info != null || (GameMain.Client?.CharacterInfo != null && GameMain.GameSession?.GameMode is MultiPlayerCampaign);
talentsButton.Enabled = Character.Controlled?.Info != null || GameMain.Client?.CharacterInfo != null;
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
{
SelectInfoFrameTab(InfoFrameTab.Crew);
@@ -453,7 +453,8 @@ namespace Barotrauma
GUIListBox crewList = new GUIListBox(new RectTransform(crewListSize, content.RectTransform))
{
Padding = new Vector4(2, 5, 0, 0),
AutoHideScrollBar = false
AutoHideScrollBar = false,
PlaySoundOnSelect = true
};
crewList.UpdateDimensions();
@@ -928,8 +929,8 @@ namespace Barotrauma
}
else
{
Vector2 stringOffset = GUIStyle.GlobalFont.MeasureString(inLobbyString) / 2f;
GUIStyle.GlobalFont.DrawString(spriteBatch, inLobbyString, area.Center.ToVector2() - stringOffset, Color.White);
Vector2 stringOffset = GUIStyle.Font.MeasureString(inLobbyString) / 2f;
GUIStyle.Font.DrawString(spriteBatch, inLobbyString, area.Center.ToVector2() - stringOffset, Color.White);
}
}
@@ -1914,6 +1915,7 @@ namespace Barotrauma
{
OnClicked = (button, o) =>
{
GameMain.Client?.SendCharacterInfo();
characterSettingsFrame!.Visible = false;
talentFrameMain.Visible = true;
return true;
@@ -462,7 +462,7 @@ namespace Barotrauma
button.Enabled = false;
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -497,7 +497,7 @@ namespace Barotrauma
button.Enabled = false;
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -539,7 +539,7 @@ namespace Barotrauma
GameMain.Client?.SendCampaignState();
}
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
}
else
{
@@ -589,7 +589,7 @@ namespace Barotrauma
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate();
buyButtonLayout.Recalculate();
@@ -622,7 +622,8 @@ namespace Barotrauma
PadBottom = true,
SelectTop = true,
ClampScrollToElements = true,
Spacing = 8
Spacing = 8,
PlaySoundOnSelect = true
};
Dictionary<UpgradeCategory, List<UpgradePrefab>> upgrades = new Dictionary<UpgradeCategory, List<UpgradePrefab>>();
@@ -1123,7 +1124,10 @@ namespace Barotrauma
{
priceText.Text = string.Empty;
}
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle) { Enabled = false };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle)
{
Enabled = false
};
if (upgradePrefab != null)
{
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
@@ -1212,7 +1216,7 @@ namespace Barotrauma
Campaign.UpgradeManager.PurchaseUpgrade(prefab, category);
GameMain.Client?.SendCampaignState();
return true;
});
}, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
return true;
};
@@ -1400,7 +1404,7 @@ namespace Barotrauma
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{
ScrollToCategory(data => data.Category.IsWallUpgrade);
ScrollToCategory(data => data.Category.IsWallUpgrade, GUIListBox.PlaySelectSound.Yes);
}
}
}
@@ -1682,7 +1686,7 @@ namespace Barotrauma
}
}
private void ScrollToCategory(Predicate<CategoryData> predicate)
private void ScrollToCategory(Predicate<CategoryData> predicate, GUIListBox.PlaySelectSound playSelectSound = GUIListBox.PlaySelectSound.No)
{
if (currentStoreLayout == null) { return; }
@@ -1690,7 +1694,7 @@ namespace Barotrauma
{
if (child.UserData is CategoryData data && predicate(data))
{
currentStoreLayout.ScrollToElement(child);
currentStoreLayout.ScrollToElement(child, playSelectSound);
break;
}
}
@@ -26,7 +26,7 @@ namespace Barotrauma
private Color SubmarineColor => GUIStyle.Orange;
private Point createdForResolution;
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
public static VotingInterface CreateSubmarineVotingInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float votingTime)
{
if (starter == null || info == null) { return null; }
@@ -38,7 +38,7 @@ namespace Barotrauma
getMaxVotes = () => GameMain.NetworkMember?.Voting?.GetVoteCountMax(type) ?? 0,
};
subVoting.onVoteEnd = () => subVoting.SendSubmarineVoteEndMessage(info, type);
subVoting.SetSubmarineVotingText(starter, info, type);
subVoting.SetSubmarineVotingText(starter, info, transferItems, type);
subVoting.Initialize(starter, type);
return subVoting;
}
@@ -160,19 +160,21 @@ namespace Barotrauma
}
#region Submarine Voting
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, VoteType type)
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, bool transferItems, VoteType type)
{
string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
string submarineRichString = $"‖color:{SubmarineColor.R},{SubmarineColor.G},{SubmarineColor.B}‖{info.DisplayName}‖color:end‖";
string tag = string.Empty;
LocalizedString text = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
text = TextManager.GetWithVariables("submarinepurchaseandswitchvote",
tag = transferItems ? "submarinepurchaseandswitchwithitemsvote" : "submarinepurchaseandswitchvote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[amount]", info.Price.ToString()),
@@ -189,7 +191,8 @@ namespace Barotrauma
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
text = TextManager.GetWithVariables("submarineswitchfeevote",
tag = transferItems ? "submarineswitchwithitemsfeevote" : "submarineswitchfeevote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString),
("[locationname]", endLocation.Name),
@@ -198,13 +201,13 @@ namespace Barotrauma
}
else
{
text = TextManager.GetWithVariables("submarineswitchnofeevote",
tag = transferItems ? "submarineswitchwithitemsnofeevote" : "submarineswitchnofeevote";
text = TextManager.GetWithVariables(tag,
("[playername]", characterRichString),
("[submarinename]", submarineRichString));
}
break;
}
votingOnText = RichString.Rich(text);
}
@@ -943,6 +943,23 @@ namespace Barotrauma
Timing.Accumulator = 0.0f;
}
private void FixRazerCortex()
{
#if WINDOWS
//Razer Cortex's overlay is broken.
//For whatever reason, it messes up the blendstate and,
//because MonoGame reasonably assumes that you don't need
//to touch it if you're setting it to the exact same one
//you were already using, it doesn't fix Razer's mess.
//Therefore, we need to change the blendstate TWICE:
//once to force MonoGame to change it, and then again to
//use the blendstate we actually want.
var oldBlendState = GraphicsDevice.BlendState;
GraphicsDevice.BlendState = oldBlendState == BlendState.Opaque ? BlendState.NonPremultiplied : BlendState.Opaque;
GraphicsDevice.BlendState = oldBlendState;
#endif
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
@@ -950,7 +967,9 @@ namespace Barotrauma
{
Stopwatch sw = new Stopwatch();
sw.Start();
FixRazerCortex();
double deltaTime = gameTime.ElapsedGameTime.TotalSeconds;
if (Timing.FrameLimit > 0)
@@ -1043,7 +1062,7 @@ namespace Barotrauma
}
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedFriendlyOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
{
spCampaign.Map.CurrentLocation.AddStock(spCampaign.CargoManager.SoldItems);
spCampaign.CargoManager.ClearSoldItemsProjSpecific();
@@ -1608,7 +1608,7 @@ namespace Barotrauma
{
if (character == Character.Controlled && crewList.SelectedComponent != characterComponent)
{
crewList.Select(character, force: true);
crewList.Select(character, GUIListBox.Force.Yes);
}
// Icon colors might change based on the target so we check if they need to be updated
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
@@ -587,196 +587,78 @@ namespace Barotrauma
//static because we may need to instantiate the campaign if it hasn't been done yet
public static void ClientRead(IReadMessage msg)
{
NetFlags requiredFlags = (NetFlags)msg.ReadUInt16();
bool isFirstRound = msg.ReadBoolean();
byte campaignID = msg.ReadByte();
UInt16 updateID = msg.ReadUInt16();
UInt16 saveID = msg.ReadUInt16();
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
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();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadIdentifier(), msg.ReadSingle());
}
bool forceMapUI = msg.ReadBoolean();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
byte missionCount = msg.ReadByte();
var availableMissions = new List<(Identifier Identifier, byte ConnectionIndex)>();
for (int i = 0; i < missionCount; i++)
{
Identifier missionIdentifier = msg.ReadIdentifier();
byte connectionIndex = msg.ReadByte();
availableMissions.Add((missionIdentifier, connectionIndex));
}
var storeBalances = new Dictionary<Identifier, UInt16>();
if (msg.ReadBoolean())
{
byte storeCount = msg.ReadByte();
for (int i = 0; i < storeCount; i++)
{
Identifier identifier = msg.ReadIdentifier();
UInt16 storeBalance = msg.ReadUInt16();
storeBalances.Add(identifier, storeBalance);
}
}
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
var purchasedItems = ReadPurchasedItems(msg, sender: null);
var soldItems = ReadSoldItems(msg);
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
ushort purchasedItemSwapCount = msg.ReadUInt16();
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
}
bool refreshCampaignUI = false;
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
{
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure, mapSeed);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty, mapSeed);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
//server has a newer save file
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
{
campaign.PendingSaveID = saveID;
}
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
{
campaign.SuppressStateSending = true;
campaign.IsFirstRound = isFirstRound;
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID)) { campaign.PendingSaveID = saveID; }
campaign.IsFirstRound = isFirstRound;
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
if (requiredFlags.HasFlag(NetFlags.Misc))
{
DebugConsole.Log("Received campaign update (Misc)");
UInt16 id = msg.ReadUInt16();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
if (ShouldApply(NetFlags.Misc, id, requireUpToDateSave: false))
{
refreshCampaignUI = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
}
}
if (requiredFlags.HasFlag(NetFlags.MapAndMissions))
{
DebugConsole.Log("Received campaign update (MapAndMissions)");
UInt16 id = msg.ReadUInt16();
bool forceMapUI = msg.ReadBoolean();
bool allowDebugTeleport = msg.ReadBoolean();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte missionCount = msg.ReadByte();
var availableMissions = new List<(Identifier Identifier, byte ConnectionIndex)>();
for (int i = 0; i < missionCount; i++)
{
Identifier missionIdentifier = msg.ReadIdentifier();
byte connectionIndex = msg.ReadByte();
availableMissions.Add((missionIdentifier, connectionIndex));
}
byte selectedMissionCount = msg.ReadByte();
List<int> selectedMissionIndices = new List<int>();
for (int i = 0; i < selectedMissionCount; i++)
{
selectedMissionIndices.Add(msg.ReadByte());
}
if (ShouldApply(NetFlags.MapAndMissions, id, requireUpToDateSave: true))
{
campaign.ForceMapUI = forceMapUI;
UpgradeStore.WaitForServerUpdate = false;
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndices);
GameMain.GameSession.OwnedSubmarines.Clear();
foreach (int ownedSubIndex in ownedSubIndices)
{
SubmarineInfo sub = GameMain.Client.ServerSubmarines[ownedSubIndex];
if (GameMain.NetLobbyScreen.CheckIfCampaignSubMatches(sub, NetLobbyScreen.SubmarineDeliveryData.Owned))
{
GameMain.GameSession.OwnedSubmarines.Add(sub);
}
}
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
foreach (var balance in storeBalances)
{
if (campaign.Map.CurrentLocation.GetStore(balance.Key) is { } store)
{
store.Balance = balance.Value;
}
}
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
if (purchasedItemSwap.ItemToInstall == null)
{
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
}
else
{
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
}
}
foreach (Item item in Item.ItemList)
{
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
{
item.PendingItemSwap = null;
}
}
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
if (faction?.Reputation != null)
{
faction.Reputation.SetReputation(rep);
}
else
{
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
}
}
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var availableMission in availableMissions)
{
MissionPrefab missionPrefab = MissionPrefab.Prefabs.Find(mp => mp.Identifier == availableMission.Identifier);
@@ -800,36 +682,268 @@ namespace Barotrauma
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
}
}
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
bool shouldRefresh = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
if (shouldRefresh)
{
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
if (myCharacterInfo != null)
{
GameMain.Client.CharacterInfo = myCharacterInfo;
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
campaign.Map.SelectMission(selectedMissionIndices);
ReadStores(msg, apply: true);
}
else
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.SubList))
{
DebugConsole.Log("Received campaign update (SubList)");
UInt16 id = msg.ReadUInt16();
ushort ownedSubCount = msg.ReadUInt16();
List<ushort> ownedSubIndices = new List<ushort>();
for (int i = 0; i < ownedSubCount; i++)
{
ownedSubIndices.Add(msg.ReadUInt16());
}
campaign.lastUpdateID = updateID;
campaign.SuppressStateSending = false;
if (ShouldApply(NetFlags.SubList, id, requireUpToDateSave: false))
{
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);
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.UpgradeManager))
{
DebugConsole.Log("Received campaign update (UpgradeManager)");
UInt16 id = msg.ReadUInt16();
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
Identifier upgradeIdentifier = msg.ReadIdentifier();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
Identifier categoryIdentifier = msg.ReadIdentifier();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
ushort purchasedItemSwapCount = msg.ReadUInt16();
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
for (int i = 0; i < purchasedItemSwapCount; i++)
{
UInt16 itemToRemoveID = msg.ReadUInt16();
Identifier itemToInstallIdentifier = msg.ReadIdentifier();
ItemPrefab itemToInstall = itemToInstallIdentifier.IsEmpty ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
if (!(Entity.FindEntityByID(itemToRemoveID) is Item itemToRemove)) { continue; }
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
}
if (ShouldApply(NetFlags.UpgradeManager, id, requireUpToDateSave: true))
{
UpgradeStore.WaitForServerUpdate = false;
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var purchasedItemSwap in purchasedItemSwaps)
{
if (purchasedItemSwap.ItemToInstall == null)
{
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
}
else
{
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
}
}
foreach (Item item in Item.ItemList)
{
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
{
item.PendingItemSwap = null;
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.ItemsInBuyCrate))
{
DebugConsole.Log("Received campaign update (ItemsInBuyCrate)");
UInt16 id = msg.ReadUInt16();
var buyCrateItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.ItemsInBuyCrate, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.SetLastUpdateIdForFlag(NetFlags.ItemsInBuyCrate, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.ItemsInSellFromSubCrate))
{
DebugConsole.Log("Received campaign update (ItemsInSellFromSubCrate)");
UInt16 id = msg.ReadUInt16();
var subSellCrateItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.ItemsInSellFromSubCrate, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetItemsInSubSellCrate(subSellCrateItems);
campaign.SetLastUpdateIdForFlag(NetFlags.ItemsInSellFromSubCrate, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.PurchasedItems))
{
DebugConsole.Log("Received campaign update (PuchasedItems)");
UInt16 id = msg.ReadUInt16();
var purchasedItems = ReadPurchasedItems(msg, sender: null);
if (ShouldApply(NetFlags.PurchasedItems, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.SetLastUpdateIdForFlag(NetFlags.PurchasedItems, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.SoldItems))
{
DebugConsole.Log("Received campaign update (SoldItems)");
UInt16 id = msg.ReadUInt16();
var soldItems = ReadSoldItems(msg);
if (ShouldApply(NetFlags.SoldItems, id, requireUpToDateSave: true))
{
campaign.CargoManager.SetSoldItems(soldItems);
campaign.SetLastUpdateIdForFlag(NetFlags.SoldItems, id);
ReadStores(msg, apply: true);
}
else
{
ReadStores(msg, apply: false);
}
}
if (requiredFlags.HasFlag(NetFlags.Reputation))
{
DebugConsole.Log("Received campaign update (Reputation)");
UInt16 id = msg.ReadUInt16();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<Identifier, float> factionReps = new Dictionary<Identifier, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadIdentifier(), msg.ReadSingle());
}
if (ShouldApply(NetFlags.Reputation, id, requireUpToDateSave: true))
{
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier == identifier);
if (faction?.Reputation != null)
{
faction.Reputation.SetReputation(rep);
}
else
{
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
}
}
}
}
if (requiredFlags.HasFlag(NetFlags.CharacterInfo))
{
DebugConsole.Log("Received campaign update (CharacterInfo)");
UInt16 id = msg.ReadUInt16();
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
}
if (ShouldApply(NetFlags.CharacterInfo, id, requireUpToDateSave: true))
{
if (myCharacterInfo != null)
{
GameMain.Client.CharacterInfo = myCharacterInfo;
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
}
else
{
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
}
}
}
campaign.SuppressStateSending = true;
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
{
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
if (refreshCampaignUI)
{
campaign?.CampaignUI?.UpgradeStore?.RequestRefresh();
}
campaign.SuppressStateSending = false;
bool ShouldApply(NetFlags flag, UInt16 id, bool requireUpToDateSave)
{
if (NetIdUtils.IdMoreRecent(id, campaign.GetLastUpdateIdForFlag(flag)) &&
(!requireUpToDateSave || saveID == campaign.LastSaveID))
{
campaign.SetLastUpdateIdForFlag(flag, id);
return true;
}
else
{
return false;
}
}
void ReadStores(IReadMessage msg, bool apply)
{
var storeBalances = new Dictionary<Identifier, UInt16>();
if (msg.ReadBoolean())
{
byte storeCount = msg.ReadByte();
for (int i = 0; i < storeCount; i++)
{
Identifier identifier = msg.ReadIdentifier();
UInt16 storeBalance = msg.ReadUInt16();
storeBalances.Add(identifier, storeBalance);
}
}
if (apply)
{
foreach (var balance in storeBalances)
{
if (campaign.Map?.CurrentLocation?.GetStore(balance.Key) is { } store)
{
store.Balance = balance.Value;
}
}
}
}
}
public void ClientReadCrew(IReadMessage msg)
@@ -58,12 +58,12 @@ namespace Barotrauma
/// <summary>
/// Instantiates a new single player campaign
/// </summary>
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign)
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
{
CampaignMetadata = new CampaignMetadata(this);
UpgradeManager = new UpgradeManager(this);
map = new Map(this, mapSeed, settings);
Settings = settings;
map = new Map(this, mapSeed);
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
@@ -79,7 +79,7 @@ namespace Barotrauma
/// <summary>
/// Loads a previously saved single player campaign from XML
/// </summary>
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign)
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty)
{
IsFirstRound = false;
@@ -87,7 +87,7 @@ namespace Barotrauma
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "campaignsettings":
case CampaignSettings.LowerCaseSaveElementName:
Settings = new CampaignSettings(subElement);
break;
case "crew":
@@ -95,7 +95,7 @@ namespace Barotrauma
ActiveOrdersElement = subElement.GetChildElement("activeorders");
break;
case "map":
map = Map.Load(this, subElement, Settings);
map = Map.Load(this, subElement);
break;
case "metadata":
CampaignMetadata = new CampaignMetadata(this, subElement);
@@ -163,21 +163,14 @@ namespace Barotrauma
/// <summary>
/// Start a completely new single player campaign
/// </summary>
public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings)
{
var campaign = new SinglePlayerCampaign(mapSeed, settings);
return campaign;
}
public static SinglePlayerCampaign StartNew(string mapSeed, CampaignSettings startingSettings) => new SinglePlayerCampaign(mapSeed, startingSettings);
/// <summary>
/// Load a previously saved single player campaign from xml
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static SinglePlayerCampaign Load(XElement element)
{
return new SinglePlayerCampaign(element);
}
public static SinglePlayerCampaign Load(XElement element) => new SinglePlayerCampaign(element);
private void InitUI()
{
@@ -64,7 +64,6 @@ namespace Barotrauma
public Vector2[] SlotPositions;
public static Point SlotSize;
public static int Spacing;
public static int HideButtonWidth;
private Layout layout;
public Layout CurrentLayout
@@ -77,64 +76,11 @@ namespace Barotrauma
SetSlotPositions(layout);
}
}
public bool Hidden { get; set; }
private bool hidePersonalSlots;
private float hidePersonalSlotsState;
private GUIButton hideButton;
private Rectangle personalSlotArea;
public bool HidePersonalSlots
{
get { return hidePersonalSlots; }
}
public Rectangle PersonalSlotArea
{
get { return personalSlotArea; }
}
private readonly GUIImage[] indicators = new GUIImage[5];
private readonly int[] indicatorIndices = new int[5];
private Vector2 indicatorSpriteSize;
private GUILayoutGroup indicatorGroup;
partial void InitProjSpecific(XElement element)
{
Hidden = true;
hideButton = new GUIButton(new RectTransform(new Point((int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f)), HUDLayoutSettings.BottomRightInfoArea.Height), GUI.Canvas)
{ AbsoluteOffset = HUDLayoutSettings.CrewArea.Location },
"", style: "EquipmentToggleButton");
indicatorGroup = new GUILayoutGroup(new RectTransform(Point.Zero, hideButton.RectTransform)) { IsHorizontal = false };
indicatorGroup.ChildAnchor = Anchor.TopCenter;
indicatorSpriteSize = GUIStyle.GetComponentStyle("EquipmentIndicatorDivingSuit").GetDefaultSprite().size;
indicators[0] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorDivingSuit");
indicators[1] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorID");
indicators[2] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorOutfit");
indicators[3] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadwear");
indicators[4] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorHeadphones");
indicatorIndices[0] = FindLimbSlot(InvSlotType.OuterClothes);
indicatorIndices[1] = FindLimbSlot(InvSlotType.Card);
indicatorIndices[2] = FindLimbSlot(InvSlotType.InnerClothes);
indicatorIndices[3] = FindLimbSlot(InvSlotType.Head);
indicatorIndices[4] = FindLimbSlot(InvSlotType.Headset);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].CanBeFocused = false;
}
hideButton.OnClicked += (GUIButton btn, object userdata) =>
{
hidePersonalSlots = !hidePersonalSlots;
return true;
};
hidePersonalSlots = false;
SlotPositions = new Vector2[SlotTypes.Length];
CurrentLayout = Layout.Default;
SetSlotPositions(layout);
@@ -271,25 +217,6 @@ namespace Barotrauma
return false;
}
private void SetIndicatorSizes()
{
indicatorGroup.RectTransform.AbsoluteOffset = new Point((int)Math.Round(4 * GUI.Scale), (int)Math.Round(7 * GUI.Scale));
indicatorGroup.RectTransform.NonScaledSize = new Point(hideButton.Rect.Width - indicatorGroup.RectTransform.AbsoluteOffset.X * 2, hideButton.Rect.Height - indicatorGroup.RectTransform.AbsoluteOffset.Y * 2);
indicatorGroup.AbsoluteSpacing = (int)Math.Ceiling(2 * GUI.Scale);
int indicatorHeight = (indicatorGroup.RectTransform.NonScaledSize.Y - indicatorGroup.AbsoluteSpacing * (indicators.Length - 1)) / indicators.Length;
int indicatorWidth = (int)(indicatorSpriteSize.X / (indicatorSpriteSize.Y / indicatorHeight));
if (HideButtonWidth % 2 != indicatorWidth % 2) indicatorWidth++;
Point indicatorSize = new Point(indicatorWidth, indicatorHeight);
for (int i = 0; i < indicators.Length; i++)
{
indicators[i].RectTransform.NonScaledSize = indicatorSize;
}
}
private void SetSlotPositions(Layout layout)
{
bool isFourByThree = GUI.IsFourByThree();
@@ -302,13 +229,9 @@ namespace Barotrauma
Spacing = (int)(8 * UIScale);
}
HideButtonWidth = (int)(31f * (HUDLayoutSettings.BottomRightInfoArea.Height / 100f));
SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint();
int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight;
hideButton.Visible = false;
if (visualSlots == null) { CreateSlots(); }
if (visualSlots.None()) { return; }
@@ -320,7 +243,7 @@ namespace Barotrauma
int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s) && s != InvSlotType.HealthInterface);
int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (SlotSize.X + Spacing) / 2;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing * 4 - HideButtonWidth;
int upperX = HUDLayoutSettings.BottomRightInfoArea.X - SlotSize.X - Spacing;
//make sure the rightmost normal slot doesn't overlap with the personal slots
x -= Math.Max((x + normalSlotCount * (SlotSize.X + Spacing)) - (upperX - personalSlotCount * (SlotSize.X + Spacing)), 0);
@@ -343,16 +266,6 @@ namespace Barotrauma
x += SlotSize.X + Spacing;
}
}
if (hideButtonSlotIndex > -1)
{
hideButton.RectTransform.SetPosition(Anchor.TopLeft, Pivot.TopLeft);
hideButton.RectTransform.NonScaledSize = new Point(HideButtonWidth, HUDLayoutSettings.BottomRightInfoArea.Height);
hideButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.BottomRightInfoArea.Left - HideButtonWidth + GUI.IntScaleCeiling(2f), HUDLayoutSettings.BottomRightInfoArea.Y + GUI.IntScaleCeiling(1f));
hideButton.Visible = Screen.Selected != GameMain.SubEditorScreen || !GameMain.SubEditorScreen.WiringMode;
SetIndicatorSizes();
}
}
break;
case Layout.Right:
@@ -533,58 +446,13 @@ namespace Barotrauma
bool hoverOnInventory = GUI.MouseOn == null &&
((selectedSlot != null && selectedSlot.IsSubSlot) || (DraggingItems.Any() && (DraggingSlot == null || !DraggingSlot.MouseOn())));
if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true;
if (layout == Layout.Default && (Screen.Selected != GameMain.SubEditorScreen || Screen.Selected is SubEditorScreen editor && editor.WiringMode))
{
if (hideButton.Visible)
{
hideButton.AddToGUIUpdateList();
hideButton.UpdateManually(deltaTime, alsoChildren: true);
hidePersonalSlotsState = hidePersonalSlots ?
Math.Min(hidePersonalSlotsState + deltaTime * 5.0f, 1.0f) :
Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f);
bool personalSlotsMoving = hidePersonalSlotsState > 0 && hidePersonalSlotsState < 1f;
for (int i = 0; i < visualSlots.Length; i++)
{
if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; }
if (HidePersonalSlots)
{
if (selectedSlot?.Slot == visualSlots[i]) { selectedSlot = null; }
highlightedSubInventorySlots.RemoveWhere(s => s.Slot == visualSlots[i]);
}
visualSlots[i].IsMoving = personalSlotsMoving;
visualSlots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState);
}
}
}
if (CharacterHealth.OpenHealthWindow != null) { hoverOnInventory = true; }
if (hoverOnInventory) { HideTimer = 0.5f; }
if (HideTimer > 0.0f) { HideTimer -= deltaTime; }
UpdateSlotInput();
//force personal slots open if an item is running out of battery/fuel/oxygen/etc
if (hidePersonalSlots)
{
for (int i = 0; i < visualSlots.Length; i++)
{
var item = slots[i].FirstOrDefault();
if (item?.OwnInventory != null && item.OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i]))
{
var containedItem = item.OwnInventory.AllItems.FirstOrDefault();
if (containedItem != null &&
containedItem.Condition > 0.0f &&
containedItem.Condition / containedItem.MaxCondition < 0.15f)
{
hidePersonalSlots = false;
}
}
}
}
hideSubInventories.Clear();
//remove highlighted subinventory slots that can no longer be accessed
highlightedSubInventorySlots.RemoveWhere(s =>
@@ -653,8 +521,6 @@ namespace Barotrauma
if (character == Character.Controlled && character.SelectedCharacter == null) // Permanently open subinventories only available when the default UI layout is in use -> not when grabbing characters
{
UpdateEquipmentIndicators();
//remove the highlighted slots of other characters' inventories when not grabbing anyone
highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory != this && s.ParentInventory?.Owner is Character);
@@ -799,40 +665,6 @@ namespace Barotrauma
}
}
}
private void UpdateEquipmentIndicators()
{
for (int i = 0; i < indicators.Length; i++)
{
if (indicatorIndices[i] < 0) { continue; }
Item item = slots[indicatorIndices[i]].FirstOrDefault();
if (item != null)
{
Wearable wearable = item.GetComponent<Wearable>();
if (wearable != null && wearable.DisplayContainedStatus)
{
float conditionPercentage = item.GetContainedItemConditionPercentage();
if (conditionPercentage != -1)
{
indicators[i].Color = ToolBox.GradientLerp(conditionPercentage, GUIStyle.EquipmentIndicatorRunningOut, GUIStyle.EquipmentIndicatorEquipped);
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorRunningOut;
}
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorEquipped;
}
}
else
{
indicators[i].Color = GUIStyle.EquipmentIndicatorNotEquipped;
}
}
}
private void ShowSubInventory(SlotReference slotRef, float deltaTime, Camera cam, List<SlotReference> hideSubInventories, bool isEquippedSubInventory)
{
@@ -942,6 +774,7 @@ namespace Barotrauma
}
else
{
bool isEquippable = item.AllowedSlots.Any(s => s != InvSlotType.Any);
var selectedContainer = character.SelectedConstruction?.GetComponent<ItemContainer>();
if (selectedContainer != null &&
selectedContainer.Inventory != null &&
@@ -967,8 +800,9 @@ namespace Barotrauma
return QuickUseAction.TakeFromCharacter;
}
else if (character.HeldItems.Any(i =>
i.OwnInventory != null &&
((i.OwnInventory.CanBePut(item) && allowInventorySwap) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
i.OwnInventory != null &&
/*disallow putting into equipped item if the item is equippable (equip as the quick action instead)*/
((i.OwnInventory.CanBePut(item) && (allowInventorySwap || !isEquippable)) || (i.OwnInventory.Capacity == 1 && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))))
{
return QuickUseAction.PutToEquippedItem;
}
@@ -1131,11 +965,18 @@ namespace Barotrauma
}
break;
case QuickUseAction.PutToEquippedItem:
foreach (Item heldItem in character.HeldItems)
{
if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
//(in that case, the quick action should just fill up the stack)
bool disallowSwapping =
heldItem.OwnInventory.Capacity == 1 &&
heldItem.OwnInventory.GetItemAt(0)?.Prefab == item.Prefab &&
heldItem.OwnInventory.GetItemsAt(0).Count() > 1;
if (heldItem.OwnInventory.TryPutItem(item, Character.Controlled) ||
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: true, allowCombine: false, user: Character.Controlled)))
(heldItem.OwnInventory.Capacity == 1 && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: !disallowSwapping, allowCombine: false, user: Character.Controlled)))
{
success = true;
for (int j = 0; j < capacity; j++)
@@ -1197,11 +1038,6 @@ namespace Barotrauma
DrawSlot(spriteBatch, this, visualSlots[i], slots[i].FirstOrDefault(), i, drawItem, SlotTypes[i]);
}
if (hideButton != null && hideButton.Visible && !Locked)
{
hideButton.DrawManually(spriteBatch, alsoChildren: true);
}
VisualSlot highlightedQuickUseSlot = null;
Rectangle inventoryArea = Rectangle.Empty;
@@ -203,7 +203,7 @@ namespace Barotrauma.Items.Components
private float lastMuffleCheckTime;
private ItemSound loopingSound;
private SoundChannel loopingSoundChannel;
private List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
private readonly List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
public ItemComponent ReplacedBy;
public ItemComponent GetReplacementOrThis()
@@ -211,13 +211,16 @@ namespace Barotrauma.Items.Components
return ReplacedBy?.GetReplacementOrThis() ?? this;
}
public bool NeedsSoundUpdate()
{
if (hasSoundsOfType[(int)ActionType.Always]) { return true; }
if (loopingSoundChannel != null && loopingSoundChannel.IsPlaying) { return true; }
if (playingOneshotSoundChannels.Count > 0) { return true; }
return false;
}
public void UpdateSounds()
{
if (!isActive || item.Condition <= 0.0f)
{
StopSounds(ActionType.OnActive);
}
if (loopingSound != null && loopingSoundChannel != null && loopingSoundChannel.IsPlaying)
{
if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
@@ -280,6 +283,7 @@ namespace Barotrauma.Items.Components
loopingSound.RoundSound.GetRandomFrequencyMultiplier(),
SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull));
loopingSoundChannel.Looping = true;
item.CheckNeedsSoundUpdate(this);
//TODO: tweak
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
loopingSoundChannel.Far = loopingSound.Range;
@@ -298,7 +302,6 @@ namespace Barotrauma.Items.Components
loopingSound = null;
}
}
return;
}
@@ -333,6 +336,7 @@ namespace Barotrauma.Items.Components
}
PlaySound(matchingSounds[index], item.WorldPosition);
item.CheckNeedsSoundUpdate(this);
}
}
private void PlaySound(ItemSound itemSound, Vector2 position)
@@ -108,6 +108,7 @@ namespace Barotrauma.Items.Components
itemList = new GUIListBox(new RectTransform(new Vector2(1f, 0.9f), paddedItemFrame.RectTransform), style: null)
{
PlaySoundOnSelect = true,
OnSelected = (component, userdata) =>
{
selectedItem = userdata as FabricationRecipe;
@@ -333,6 +333,7 @@ namespace Barotrauma.Items.Components
GUIListBox listBox = new GUIListBox(new RectTransform(Vector2.One, searchAutoComplete.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (component, o) =>
{
if (o is ItemPrefab prefab)
@@ -744,11 +745,11 @@ namespace Barotrauma.Items.Components
if (key == Keys.Down)
{
listBox.SelectNext(true, autoScroll: true);
listBox.SelectNext(force: GUIListBox.Force.Yes, playSelectSound: GUIListBox.PlaySelectSound.Yes);
}
else if (key == Keys.Up)
{
listBox.SelectPrevious(true, autoScroll: true);
listBox.SelectPrevious(force: GUIListBox.Force.Yes, playSelectSound: GUIListBox.PlaySelectSound.Yes);
}
else if (key == Keys.Enter)
{
@@ -782,7 +783,7 @@ namespace Barotrauma.Items.Components
if (component.Visible && first)
{
listBox.Select(i, force: true, autoScroll: false);
listBox.Select(i, GUIListBox.Force.Yes, GUIListBox.AutoScroll.Disabled);
first = false;
}
}
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
}
GuiFrame = selectionUI.GuiFrame;
selectionUI.RefreshSubmarineDisplay(true);
selectionUI.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
IsActive = true;
return base.Select(character);
}
@@ -927,6 +927,8 @@ namespace Barotrauma.Items.Components
bool autoPilot = msg.ReadBoolean();
bool dockingButtonClicked = msg.ReadBoolean();
ushort userID = msg.ReadUInt16();
Vector2 newSteeringInput = steeringInput;
Vector2 newTargetVelocity = targetVelocity;
float newSteeringAdjustSpeed = steeringAdjustSpeed;
@@ -935,7 +937,7 @@ namespace Barotrauma.Items.Components
if (dockingButtonClicked)
{
item.SendSignal("1", "toggle_docking");
item.SendSignal(new Signal("1", sender: Entity.FindEntityByID(userID) as Character), "toggle_docking");
}
if (autoPilot)
@@ -40,8 +40,6 @@ namespace Barotrauma.Items.Components
}
}
private LightComponent lightComponent;
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
{
for (var i = 0; i < GrowableSeeds.Length; i++)
@@ -418,7 +418,7 @@ namespace Barotrauma.Items.Components
if (!GameMain.IsMultiplayer) { RepairBoost(qteSuccess); }
SoundPlayer.PlayUISound(qteSuccess ? GUISoundType.IncreaseQuantity : GUISoundType.DecreaseQuantity);
SoundPlayer.PlayUISound(qteSuccess ? GUISoundType.Increase : GUISoundType.Decrease);
//on failure during cooldown reset cursor to beginning
if (!qteSuccess && qteCooldown > 0.0f) { qteTimer = QteDuration; }
@@ -100,7 +100,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
"> " + input,
textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.GlobalFont)
textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.Font)
{
CanBeFocused = false
};
@@ -569,6 +569,18 @@ namespace Barotrauma
}
}
public void CheckNeedsSoundUpdate(ItemComponent ic)
{
if (ic.NeedsSoundUpdate())
{
if (!updateableComponents.Contains(ic))
{
updateableComponents.Add(ic);
}
isActive = true;
}
}
public void UpdateSpriteStates(float deltaTime)
{
if (activeContainedSprite != null)
@@ -940,6 +952,7 @@ namespace Barotrauma
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
{
PlaySoundOnSelect = true,
OnSelected = (component, userData) =>
{
if (!(userData is Identifier)) { return true; }
@@ -98,7 +98,7 @@ namespace Barotrauma
OnClicked = (btn, userData) =>
{
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
Generate();
Generate(GameMain.GameSession.GameMode is CampaignMode campaign ? campaign.Settings : CampaignSettings.Empty);
InitProjectSpecific();
return true;
}
@@ -642,11 +642,11 @@ namespace Barotrauma
}
}
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()))
if (GameMain.DebugDraw)
{
if (location.Reputation != null)
Vector2 dPos = pos;
if (location == HighlightedLocation && (!location.Discovered || !location.HasOutpost()) && location.Reputation != null)
{
Vector2 dPos = pos;
dPos.Y += 48;
string name = $"Reputation: {location.Name}";
Vector2 nameSize = GUIStyle.SmallFont.MeasureString(name);
@@ -663,6 +663,8 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, dPos + (new Vector2(256, 32) / 2) - (repValueSize / 2), reputationValue, Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32), Color.White);
}
dPos.Y += 48;
GUI.DrawString(spriteBatch, dPos, $"Difficulty: {location.LevelData.Difficulty.FormatZeroDecimal()}", Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
}
}
}
@@ -154,13 +154,13 @@ namespace Barotrauma
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
if (RecommendedCrewExperience != CrewExperienceLevel.Unknown)
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
TextManager.Get(RecommendedCrewExperience.ToIdentifier()), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}
@@ -100,12 +100,16 @@ namespace Barotrauma
GUIListBox specsContainer = null;
new GUICustomComponent(new RectTransform(Vector2.One, innerPadded.RectTransform, Anchor.Center),
(spriteBatch, component) => {
(spriteBatch, component) =>
{
if (isDisposed) { return; }
camera.UpdateTransform(interpolate: true, updateListener: false);
Rectangle drawRect = new Rectangle(component.Rect.X + 1, component.Rect.Y + 1, component.Rect.Width - 2, component.Rect.Height - 2);
RenderSubmarine(spriteBatch, drawRect, component);
},
(deltaTime, component) => {
(deltaTime, component) =>
{
if (isDisposed) { return; }
bool isMouseOnComponent = GUI.MouseOn == component;
camera.MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub: false);
if (isMouseOnComponent &&
@@ -294,8 +298,8 @@ namespace Barotrauma
private void BakeMapEntity(XElement element)
{
string identifier = element.GetAttributeString("identifier", "");
if (string.IsNullOrEmpty(identifier)) { return; }
Identifier identifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
if (identifier.IsEmpty) { return; }
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
if (rect.Equals(Rectangle.Empty)) { return; }
@@ -308,7 +312,16 @@ namespace Barotrauma
float rotation = element.GetAttributeFloat("rotation", 0f);
MapEntityPrefab prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier == identifier);
MapEntityPrefab prefab = null;
if (element.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase) &&
ItemPrefab.Prefabs.TryGet(identifier, out ItemPrefab ip))
{
prefab = ip;
}
else
{
prefab = MapEntityPrefab.List.FirstOrDefault(p => p.Identifier == identifier);
}
if (prefab == null) { return; }
var texture = prefab.Sprite.Texture;
@@ -329,7 +342,6 @@ namespace Barotrauma
bool overrideSprite = false;
ItemPrefab itemPrefab = prefab as ItemPrefab;
StructurePrefab structurePrefab = prefab as StructurePrefab;
if (itemPrefab != null)
{
BakeItemComponents(itemPrefab, rect, color, scale, rotation, depth, out overrideSprite);
@@ -337,7 +349,7 @@ namespace Barotrauma
if (!overrideSprite)
{
if (structurePrefab != null)
if (prefab is StructurePrefab structurePrefab)
{
ParseUpgrades(structurePrefab.ConfigElement, ref scale);
@@ -689,7 +689,7 @@ namespace Barotrauma.Networking
if (ChildServerRelay.Process?.HasExited ?? true)
{
Disconnect();
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text.Text == ChildServerRelay.CrashMessage))
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
{
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
@@ -824,7 +824,11 @@ namespace Barotrauma.Networking
byte campaignID = inc.ReadByte();
UInt16 campaignSaveID = inc.ReadUInt16();
UInt16 campaignUpdateID = inc.ReadUInt16();
Dictionary<MultiPlayerCampaign.NetFlags, UInt16> campaignUpdateIDs = new Dictionary<MultiPlayerCampaign.NetFlags, ushort>();
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
campaignUpdateIDs[flag] = inc.ReadUInt16();
}
IWriteMessage readyToStartMsg = new WriteOnlyMessage();
readyToStartMsg.Write((byte)ClientPacketHeader.RESPONSE_STARTGAME);
@@ -843,7 +847,7 @@ namespace Barotrauma.Networking
campaign != null &&
campaign.CampaignID == campaignID &&
campaign.LastSaveID == campaignSaveID &&
campaign.LastUpdateID == campaignUpdateID;
campaignUpdateIDs.All(kvp => campaign.GetLastUpdateIdForFlag(kvp.Key) == kvp.Value);
}
readyToStartMsg.Write(readyToStart);
@@ -2401,7 +2405,10 @@ namespace Barotrauma.Networking
{
outmsg.Write(campaign.LastSaveID);
outmsg.Write(campaign.CampaignID);
outmsg.Write(campaign.LastUpdateID);
foreach (MultiPlayerCampaign.NetFlags netFlag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.Write(campaign.GetLastUpdateIdForFlag(netFlag));
}
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
@@ -2446,7 +2453,10 @@ namespace Barotrauma.Networking
{
outmsg.Write(campaign.LastSaveID);
outmsg.Write(campaign.CampaignID);
outmsg.Write(campaign.LastUpdateID);
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
outmsg.Write(campaign.GetLastUpdateIdForFlag(flag));
}
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
}
@@ -2644,7 +2654,7 @@ namespace Barotrauma.Networking
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID)
{
string savePath = transfer.FilePath;
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Empty);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
@@ -2674,9 +2684,12 @@ namespace Barotrauma.Networking
}
DebugConsole.Log("Campaign save received (" + GameMain.GameSession.SavePath + "), save ID " + campaign.LastSaveID);
//decrement campaign update ID so the server will send us the latest data
//decrement campaign update IDs so the server will send us the latest data
//(as there may have been campaign updates after the save file was created)
campaign.LastUpdateID--;
foreach (MultiPlayerCampaign.NetFlags flag in Enum.GetValues(typeof(MultiPlayerCampaign.NetFlags)))
{
campaign.SetLastUpdateIdForFlag(flag, (ushort)(campaign.GetLastUpdateIdForFlag(flag) - 1));
}
break;
case FileTransferType.Mod:
if (!(Screen.Selected is ModDownloadScreen)) { return; }
@@ -2775,6 +2788,15 @@ namespace Barotrauma.Networking
GameMain.GameSession = null;
}
public void SendCharacterInfo()
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.UPDATE_CHARACTERINFO);
WriteCharacterInfo(msg);
msg.Write((byte)ServerNetObject.END_OF_MESSAGE);
clientPeer?.Send(msg, DeliveryMethod.Reliable);
}
public void WriteCharacterInfo(IWriteMessage msg)
{
msg.Write(characterInfo == null);
@@ -2824,18 +2846,18 @@ namespace Barotrauma.Networking
}
#region Submarine Change Voting
public void InitiateSubmarineChange(SubmarineInfo sub, VoteType voteType)
public void InitiateSubmarineChange(SubmarineInfo sub, bool transferItems, VoteType voteType)
{
if (sub == null) { return; }
Vote(voteType, sub);
Vote(voteType, (sub, transferItems));
}
public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, float timeOut)
public void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float timeOut)
{
if (info == null) { return; }
if (votingInterface != null && votingInterface.VoteRunning) { return; }
votingInterface?.Remove();
votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, timeOut);
votingInterface = VotingInterface.CreateSubmarineVotingInterface(starter, info, type, transferItems, timeOut);
}
#endregion
@@ -3014,7 +3036,7 @@ namespace Barotrauma.Networking
msg.Write(mapSeed);
msg.Write(sub.Name);
msg.Write(sub.MD5Hash.StringRepresentation);
settings.Serialize(msg);
msg.Write(settings);
clientPeer.Send(msg, DeliveryMethod.Reliable);
}
@@ -111,7 +111,7 @@ namespace Barotrauma.Networking
timeout = Screen.Selected == GameMain.GameScreen ?
NetworkConnection.TimeoutThresholdInGame :
NetworkConnection.TimeoutThreshold;
PacketHeader packetHeader = (PacketHeader)data[0];
if (!packetHeader.IsServerMessage()) { return; }
@@ -11,7 +11,13 @@ namespace Barotrauma.Networking
private bool isActive;
private readonly UInt64 selfSteamID;
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey);
private UInt64 ReadSteamId(IReadMessage inc)
=> inc.ReadUInt64() ^ ownerKey64;
private void WriteSteamId(IWriteMessage msg, UInt64 val)
=> msg.Write(val ^ ownerKey64);
private long sentBytes, receivedBytes;
class RemotePeer
@@ -58,6 +64,8 @@ namespace Barotrauma.Networking
{
if (isActive) { return; }
this.ownerKey = ownerKey;
initializationStep = ConnectionInitialization.SteamTicketAndVersion;
ServerConnection = new PipeConnection(selfSteamID);
@@ -103,7 +111,7 @@ namespace Barotrauma.Networking
//known now
int prevBitPosition = msg.Message.BitPosition;
msg.Message.BitPosition = sizeof(ulong) * 8;
msg.Message.Write(ownerID);
WriteSteamId(msg.Message, ownerID);
msg.Message.BitPosition = prevBitPosition;
byte[] msgToSend = (byte[])msg.Message.Buffer.Clone();
Array.Resize(ref msgToSend, msg.Message.LengthBytes);
@@ -141,8 +149,8 @@ namespace Barotrauma.Networking
}
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(steamId);
outMsg.Write(remotePeer.OwnerSteamID);
WriteSteamId(outMsg, steamId);
WriteSteamId(outMsg, remotePeer.OwnerSteamID);
outMsg.Write(data, 1, dataLength - 1);
DeliveryMethod deliveryMethod = (DeliveryMethod)data[0];
@@ -232,7 +240,7 @@ namespace Barotrauma.Networking
{
if (!isActive) { return; }
UInt64 recipientSteamId = inc.ReadUInt64();
UInt64 recipientSteamId = ReadSteamId(inc);
DeliveryMethod deliveryMethod = (DeliveryMethod)inc.ReadByte();
int p2pDataStart = inc.BytePosition;
@@ -343,8 +351,8 @@ namespace Barotrauma.Networking
if (packetHeader.IsConnectionInitializationStep())
{
IWriteMessage outMsg = new WriteOnlyMessage();
outMsg.Write(selfSteamID);
outMsg.Write(selfSteamID);
WriteSteamId(outMsg, selfSteamID);
WriteSteamId(outMsg, selfSteamID);
outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
outMsg.Write(Name);
@@ -436,8 +444,8 @@ namespace Barotrauma.Networking
IWriteMessage msgToSend = new WriteOnlyMessage();
byte[] msgData = new byte[msg.LengthBytes];
msg.PrepareForSending(ref msgData, compressPastThreshold, out bool isCompressed, out int length);
msgToSend.Write(selfSteamID);
msgToSend.Write(selfSteamID);
WriteSteamId(msgToSend, selfSteamID);
WriteSteamId(msgToSend, selfSteamID);
msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None));
msgToSend.Write((UInt16)length);
msgToSend.Write(msgData, 0, length);
@@ -7,6 +7,20 @@ namespace Barotrauma
{
partial class Voting
{
private struct SubmarineVoteInfo
{
public SubmarineInfo SubmarineInfo { get; set; }
public bool TransferItems { get; set; }
public int DeliveryFee { get; set; }
public SubmarineVoteInfo(SubmarineInfo submarineInfo, bool transferItems, int deliveryFee)
{
SubmarineInfo = submarineInfo;
TransferItems = transferItems;
DeliveryFee = deliveryFee;
}
}
private readonly Dictionary<VoteType, int>
voteCountYes = new Dictionary<VoteType, int>(),
voteCountNo = new Dictionary<VoteType, int>(),
@@ -131,14 +145,16 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub:
case VoteType.PurchaseSub:
case VoteType.SwitchSub:
if (data is SubmarineInfo voteSub)
if (data is (SubmarineInfo voteSub, bool transferItems))
{
//initiate sub vote
msg.Write(true);
msg.Write(voteSub.Name);
msg.Write(transferItems);
}
else
{
// vote
if (!(data is int)) { return; }
msg.Write(false);
msg.Write((int)data);
@@ -246,7 +262,7 @@ namespace Barotrauma
float timeOut = inc.ReadByte();
Client myClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == GameMain.Client.ID);
if (!myClient.InGame) { return; }
if (myClient == null || !myClient.InGame) { return; }
switch (voteType)
{
@@ -254,13 +270,14 @@ namespace Barotrauma
case VoteType.PurchaseAndSwitchSub:
case VoteType.SwitchSub:
string subName1 = inc.ReadString();
bool transferItems = inc.ReadBoolean();
SubmarineInfo info = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1);
if (info == null)
{
DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
return;
}
GameMain.Client.ShowSubmarineChangeVoteInterface(starterClient, info, voteType, timeOut);
GameMain.Client.ShowSubmarineChangeVoteInterface(starterClient, info, voteType, transferItems, timeOut);
break;
case VoteType.TransferMoney:
byte fromClientId = inc.ReadByte();
@@ -279,39 +296,40 @@ namespace Barotrauma
case VoteState.Passed:
case VoteState.Failed:
bool passed = inc.ReadBoolean();
SubmarineInfo subInfo = null;
SubmarineVoteInfo submarineVoteInfo = default;
switch (voteType)
{
case VoteType.PurchaseSub:
case VoteType.PurchaseAndSwitchSub:
case VoteType.SwitchSub:
string subName2 = inc.ReadString();
subInfo = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
if (subInfo == null)
var submarineInfo = GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
bool transferItems = inc.ReadBoolean();
int deliveryFee = inc.ReadInt16();
if (submarineInfo == null)
{
DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
return;
}
submarineVoteInfo = new SubmarineVoteInfo(submarineInfo, transferItems, deliveryFee);
break;
}
GameMain.Client.VotingInterface?.EndVote(passed, yesClientCount, noClientCount);
if (passed && subInfo != null)
if (passed && submarineVoteInfo.SubmarineInfo is { } subInfo)
{
int deliveryFee = inc.ReadInt16();
switch (voteType)
{
case VoteType.PurchaseAndSwitchSub:
GameMain.GameSession.PurchaseSubmarine(subInfo);
GameMain.GameSession.SwitchSubmarine(subInfo, 0);
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems, 0);
break;
case VoteType.PurchaseSub:
GameMain.GameSession.PurchaseSubmarine(subInfo);
break;
case VoteType.SwitchSub:
GameMain.GameSession.SwitchSubmarine(subInfo, deliveryFee);
GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems, submarineVoteInfo.DeliveryFee);
break;
}
@@ -1,8 +1,11 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using Barotrauma.IO;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -15,11 +18,11 @@ namespace Barotrauma
protected GUITextBox saveNameBox, seedBox;
protected GUIButton loadGameButton;
public Action<SubmarineInfo, string, string, CampaignSettings> StartNewGame;
public Action<string> LoadGame;
protected enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 };
protected enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 }
protected CategoryFilter subFilter = CategoryFilter.All;
public GUIButton StartButton
@@ -33,15 +36,11 @@ namespace Barotrauma
get;
protected set;
}
public GUITickBox EnableRadiationToggle { get; set; }
public GUILayoutGroup CampaignSettingsContent { get; set; }
public CampaignSettings CurrentSettings = new CampaignSettings(element: null);
public GUIButton CampaignCustomizeButton { get; set; }
public GUIMessageBox CampaignCustomizeSettings { get; set; }
public GUITextBlock MaxMissionCountText;
public CampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer)
{
this.newGameContainer = newGameContainer;
@@ -102,5 +101,259 @@ namespace Barotrauma
return saveFrame;
}
public struct CampaignSettingElements
{
public SettingValue<bool> RadiationEnabled;
public SettingValue<int> MaxMissionCount;
public SettingValue<StartingBalanceAmount> StartingFunds;
public SettingValue<GameDifficulty> Difficulty;
public SettingValue<Identifier> StartItemSet;
public CampaignSettings CreateSettings()
{
return new CampaignSettings(element: null)
{
RadiationEnabled = RadiationEnabled.GetValue(),
MaxMissionCount = MaxMissionCount.GetValue(),
StartingBalanceAmount = StartingFunds.GetValue(),
Difficulty = Difficulty.GetValue(),
StartItemSet = StartItemSet.GetValue()
};
}
}
public readonly struct SettingValue<T>
{
private readonly Func<T> getter;
private readonly Action<T> setter;
public T GetValue()
{
return getter.Invoke();
}
public void SetValue(T value)
{
setter.Invoke(value);
}
public SettingValue(Func<T> get, Action<T> set)
{
getter = get;
setter = set;
}
}
private readonly struct SettingCarouselElement<T>
{
public readonly LocalizedString Label;
public readonly T Value;
public readonly bool IsHidden;
public SettingCarouselElement(T value, string label, bool isHidden = false)
{
Value = value;
Label = TextManager.Get(label).Fallback(label);
IsHidden = isHidden;
}
}
protected static CampaignSettingElements CreateCampaignSettingList(GUIComponent parent, CampaignSettings prevSettings)
{
const float verticalSize = 0.14f;
GUILayoutGroup presetDropdownLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, verticalSize), parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), TextManager.Get("campaignsettingpreset"));
GUIDropDown presetDropdown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1f), presetDropdownLayout.RectTransform), elementCount: CampaignModePresets.List.Length);
presetDropdownLayout.RectTransform.MinSize = new Point(0, presetDropdown.Rect.Height);
foreach (CampaignSettings settings in CampaignModePresets.List)
{
string name = settings.PresetName;
presetDropdown.AddItem(TextManager.Get($"preset.{name}").Fallback(name), settings);
}
GUIListBox settingsList = new GUIListBox(new RectTransform(new Vector2(1f, 1f - verticalSize), parent.RectTransform))
{
Spacing = GUI.IntScale(5)
};
SettingValue<bool> radiationEnabled = CreateTickbox(settingsList.Content, TextManager.Get("CampaignOption.EnableRadiation"), TextManager.Get("campaignoption.enableradiation.tooltip"), prevSettings.RadiationEnabled, verticalSize);
ImmutableArray<SettingCarouselElement<Identifier>> startingSetOptions = StartItemSet.Sets.OrderBy(s => s.Order).Select(set => new SettingCarouselElement<Identifier>(set.Identifier, $"startitemset.{set.Identifier}")).ToImmutableArray();
SettingCarouselElement<Identifier> prevStartingSet = startingSetOptions.FirstOrNull(element => element.Value == prevSettings.StartItemSet) ?? startingSetOptions[1];
SettingValue<Identifier> startingSetInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startitemset"), TextManager.Get("startitemsettooltip"), prevStartingSet, verticalSize, startingSetOptions);
ImmutableArray<SettingCarouselElement<StartingBalanceAmount>> fundOptions = ImmutableArray.Create(
new SettingCarouselElement<StartingBalanceAmount>(StartingBalanceAmount.High, "startingfunds.high"),
new SettingCarouselElement<StartingBalanceAmount>(StartingBalanceAmount.Medium, "startingfunds.medium"),
new SettingCarouselElement<StartingBalanceAmount>(StartingBalanceAmount.Low, "startingfunds.low")
);
SettingCarouselElement<StartingBalanceAmount> prevStartingFund = fundOptions.FirstOrNull(element => element.Value == prevSettings.StartingBalanceAmount) ?? fundOptions[1];
SettingValue<StartingBalanceAmount> startingFundsInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("startingfundsdescription"), TextManager.Get("startingfundstooltip"), prevStartingFund, verticalSize, fundOptions);
ImmutableArray<SettingCarouselElement<GameDifficulty>> difficultyOptions = ImmutableArray.Create(
new SettingCarouselElement<GameDifficulty>(GameDifficulty.Easy, "difficulty.easy"),
new SettingCarouselElement<GameDifficulty>(GameDifficulty.Medium, "difficulty.medium"),
new SettingCarouselElement<GameDifficulty>(GameDifficulty.Hard, "difficulty.hard"),
new SettingCarouselElement<GameDifficulty>(GameDifficulty.Hellish, "difficulty.hellish", isHidden: true)
);
SettingCarouselElement<GameDifficulty> prevDifficulty = difficultyOptions.FirstOrNull(element => element.Value == prevSettings.Difficulty) ?? difficultyOptions[1];
SettingValue<GameDifficulty> difficultyInput = CreateSelectionCarousel(settingsList.Content, TextManager.Get("leveldifficulty"), TextManager.Get("leveldifficultyexplanation"), prevDifficulty, verticalSize, difficultyOptions);
SettingValue<int> maxMissionCountInput = CreateGUINumberInputCarousel(settingsList.Content, TextManager.Get("maxmissioncount"), TextManager.Get("maxmissioncounttooltip"), prevSettings.MaxMissionCount, valueStep: 1, verticalSize);
presetDropdown.OnSelected = (selected, o) =>
{
if (o is CampaignSettings settings)
{
radiationEnabled.SetValue(settings.RadiationEnabled);
maxMissionCountInput.SetValue(settings.MaxMissionCount);
startingFundsInput.SetValue(settings.StartingBalanceAmount);
difficultyInput.SetValue(settings.Difficulty);
startingSetInput.SetValue(settings.StartItemSet);
return true;
}
return false;
};
return new CampaignSettingElements
{
RadiationEnabled = radiationEnabled,
MaxMissionCount = maxMissionCountInput,
StartingFunds = startingFundsInput,
Difficulty = difficultyInput,
StartItemSet = startingSetInput
};
// Create a number input with plus and minus buttons because for some reason the default GUINumberInput buttons don't work when in a GUIMessageBox
static SettingValue<int> CreateGUINumberInputCarousel(GUIComponent parent, LocalizedString description, LocalizedString tooltip, int defaultValue, int valueStep, float verticalSize)
{
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize);
GUIButton minusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton", textAlignment: Alignment.Center)
{
ClickSound = GUISoundType.Decrease,
UserData = -valueStep
};
GUINumberInput numberInput = new GUINumberInput(new RectTransform(Vector2.One, inputContainer.RectTransform, Anchor.Center), NumberType.Int, textAlignment: Alignment.Center, style: "GUITextBox",
hidePlusMinusButtons: true)
{
IntValue = defaultValue
};
inputContainer.RectTransform.Parent.MinSize = new Point(0, numberInput.RectTransform.MinSize.Y);
GUIButton plusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center)
{
ClickSound = GUISoundType.Increase,
UserData = valueStep
};
minusButton.OnClicked = plusButton.OnClicked = ChangeValue;
bool ChangeValue(GUIButton btn, object userData)
{
if (!(userData is int change)) { return false; }
numberInput.IntValue += change;
return true;
}
return new SettingValue<int>(() => numberInput.IntValue, i => numberInput.IntValue = i);
}
static SettingValue<T> CreateSelectionCarousel<T>(GUIComponent parent, LocalizedString description, LocalizedString tooltip, SettingCarouselElement<T> defaultValue, float verticalSize,
ImmutableArray<SettingCarouselElement<T>> options)
{
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, horizontalSize: 0.55f, verticalSize: verticalSize);
GUIButton minusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton", textAlignment: Alignment.Center) { UserData = -1 };
GUIFrame inputFrame = new GUIFrame(new RectTransform(Vector2.One, inputContainer.RectTransform), style: null);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(Vector2.One, inputFrame.RectTransform, Anchor.Center), NumberType.Int, textAlignment: Alignment.Center, style: "GUITextBox", hidePlusMinusButtons: true)
{
IntValue = options.IndexOf(defaultValue),
MinValueInt = 0,
MaxValueInt = options.Length,
Visible = false
};
inputContainer.RectTransform.Parent.MinSize = new Point(0, numberInput.RectTransform.MinSize.Y);
GUITextBox inputLabel = new GUITextBox(new RectTransform(Vector2.One, inputFrame.RectTransform, Anchor.Center), text: defaultValue.Label.Value, textAlignment: Alignment.Center, createPenIcon: false)
{
CanBeFocused = false
};
GUIButton plusButton = new GUIButton(new RectTransform(Vector2.One, inputContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center) { UserData = 1 };
minusButton.OnClicked = plusButton.OnClicked = ChangeValue;
bool ChangeValue(GUIButton btn, object userData)
{
if (!(userData is int change)) { return false; }
int hiddenOptions = 0;
for (int i = options.Length - 1; i >= 0; i--)
{
if (options[i].IsHidden)
{
hiddenOptions++;
continue;
}
break;
}
int limit = options.Length - hiddenOptions;
if (PlayerInput.IsShiftDown())
{
limit = options.Length;
}
int newValue = MathUtils.PositiveModulo(Math.Clamp(numberInput.IntValue + change, min: -1, max: limit), limit);
SetValue(newValue);
return true;
}
void SetValue(int value)
{
numberInput.IntValue = value;
inputLabel.Text = options[value].Label.Value;
}
return new SettingValue<T>(() => options[numberInput.IntValue].Value, t => SetValue(options.IndexOf(e => Equals(e.Value, t))));
}
static SettingValue<bool> CreateTickbox(GUIComponent parent, LocalizedString description, LocalizedString tooltip, bool defaultValue, float verticalSize)
{
GUILayoutGroup inputContainer = CreateSettingBase(parent, description, tooltip, 0.7f, verticalSize);
GUILayoutGroup tickboxContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 1.0f), inputContainer.RectTransform), childAnchor: Anchor.Center);
GUITickBox tickBox = new GUITickBox(new RectTransform(Vector2.One, tickboxContainer.RectTransform), string.Empty)
{
Selected = defaultValue,
ToolTip = tooltip
};
tickBox.Box.IgnoreLayoutGroups = true;
tickBox.Box.RectTransform.SetPosition(Anchor.CenterRight);
inputContainer.RectTransform.Parent.MinSize = new Point(0, tickBox.RectTransform.MinSize.Y);
return new SettingValue<bool>(() => tickBox.Selected, b => tickBox.Selected = b);
}
static GUILayoutGroup CreateSettingBase(GUIComponent parent, LocalizedString description, LocalizedString tooltip, float horizontalSize, float verticalSize)
{
GUILayoutGroup settingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, verticalSize), parent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(horizontalSize, 1f), settingHolder.RectTransform), description, font: parent.Rect.Width < 320 ? GUIStyle.SmallFont : GUIStyle.Font, wrap: true) { ToolTip = tooltip };
GUILayoutGroup inputContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f - horizontalSize, 0.8f), settingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
RelativeSpacing = 0.05f,
Stretch = true
};
inputContainer.RectTransform.IsFixedSize = true;
settingHolder.RectTransform.MinSize = new Point(0, (int)descriptionBlock.TextSize.Y);
return inputContainer;
}
}
}
}
@@ -18,71 +18,35 @@ namespace Barotrauma
var verticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: false)
{
Stretch = true,
RelativeSpacing = 0.0f
RelativeSpacing = 0.05f
};
GUILayoutGroup nameSeedLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), verticalLayout.RectTransform), isHorizontal: false)
{
Stretch = true
};
GUILayoutGroup campaignSettingLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), verticalLayout.RectTransform), isHorizontal: false)
{
Stretch = true,
RelativeSpacing = 0.05f
};
// New game
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, string.Empty)
{
textFilterFunction = (string str) => { return ToolBox.RemoveInvalidFileNameChars(str); }
textFilterFunction = ToolBox.RemoveInvalidFileNameChars
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), verticalLayout.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
GUIFrame radiationBoxContainer
= new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), verticalLayout.RectTransform), style: null);
GUITickBox radiationEnabledTickBox = null;
if (MapGenerationParams.Instance.RadiationParams != null)
{
radiationEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.5f), radiationBoxContainer.RectTransform, Anchor.Center), TextManager.Get("CampaignOption.EnableRadiation"), font: GUIStyle.Font)
{
Selected = true,
OnSelected = box => true
};
}
nameSeedLayout.RectTransform.MinSize = new Point(0, nameSeedLayout.Children.Sum(c => c.RectTransform.MinSize.Y));
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), verticalLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
var maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", "missions"), wrap: true)
{
ToolTip = TextManager.Get("maxmissioncounttooltip")
};
int maxMissionCount = GameMain.NetworkMember.ServerSettings.MaxMissionCount;
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
var maxMissionCountButtons = new GUIButton[2];
maxMissionCountButtons[0]
= new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform),
style: "GUIButtonToggleLeft");
var maxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), "0", textAlignment: Alignment.Center, style: "GUITextBox");
CampaignSettingElements elements = CreateCampaignSettingList(campaignSettingLayout, CampaignSettings.Empty);
void updateMissionCountText()
{
maxMissionCount = MathHelper.Clamp(maxMissionCount,
CampaignSettings.MinMissionCountLimit,
CampaignSettings.MaxMissionCountLimit);
maxMissionCountText.Text = maxMissionCount.ToString(CultureInfo.InvariantCulture);
}
maxMissionCountButtons[1]
= new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform),
style: "GUIButtonToggleRight");
maxMissionCountButtons[0].OnClicked = (button, o) =>
{
maxMissionCount--;
updateMissionCountText();
return false;
};
maxMissionCountButtons[1].OnClicked = (button, o) =>
{
maxMissionCount++;
updateMissionCountText();
return false;
};
updateMissionCountText();
maxMissionCountSettingHolder.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.04f),
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f),
verticalLayout.RectTransform) { MaxSize = new Point(int.MaxValue, 60) }, childAnchor: Anchor.BottomRight, isHorizontal: true);
StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), buttonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("StartCampaignButton"))
@@ -99,7 +63,7 @@ namespace Barotrauma
if (GameMain.NetLobbyScreen.SelectedSub == null) { return false; }
selectedSub = GameMain.NetLobbyScreen.SelectedSub;
if (selectedSub.SubmarineClass == SubmarineClass.Undefined)
{
new GUIMessageBox(TextManager.Get("error"), TextManager.Get("undefinedsubmarineselected"));
@@ -115,11 +79,7 @@ namespace Barotrauma
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = new CampaignSettings
{
RadiationEnabled = radiationEnabledTickBox?.Selected ?? GameMain.NetworkMember.ServerSettings.RadiationEnabled,
MaxMissionCount = maxMissionCount
};
CampaignSettings settings = elements.CreateSettings();
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{
@@ -172,12 +132,16 @@ namespace Barotrauma
};
StartButton.RectTransform.MaxSize = RectTransform.MaxPoint;
StartButton.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", font: GUIStyle.SmallFont, textColor: GUIStyle.Green)
{
TextGetter = () =>
{
int initialMoney = CampaignMode.InitialMoney;
int initialMoney = 8000;
if (CampaignModePresets.Definitions.TryGetValue(nameof(StartingBalanceAmount).ToIdentifier(), out var definition))
{
initialMoney = definition.GetInt(elements.StartingFunds.GetValue().ToIdentifier());
}
if (GameMain.NetLobbyScreen.SelectedSub != null)
{
initialMoney -= GameMain.NetLobbyScreen.SelectedSub.Price;
@@ -238,6 +202,7 @@ namespace Barotrauma
saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = SelectSaveFile
};
@@ -257,7 +222,7 @@ namespace Barotrauma
file1WriteTime = File.GetLastWriteTime(file1);
}
catch
{
{
//do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list
};
try
@@ -1,12 +1,11 @@
using Barotrauma.Tutorials;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using System.Globalization;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -142,7 +141,7 @@ namespace Barotrauma
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUIStyle.SubHeadingFont);
seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, ToolBox.RandomSeed(8));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SelectedSub"), font: GUIStyle.SubHeadingFont);
var moddedDropdown = new GUIDropDown(new RectTransform(new Vector2(1f, 0.02f), leftColumn.RectTransform), "", 3);
@@ -155,8 +154,12 @@ namespace Barotrauma
{
Stretch = true
};
subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform)) { ScrollBarVisible = true };
subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform))
{
PlaySoundOnSelect = true,
ScrollBarVisible = true
};
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUIStyle.Font);
var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUIStyle.Font, createClearButton: true);
@@ -191,7 +194,7 @@ namespace Barotrauma
{
TextGetter = () =>
{
int initialMoney = CampaignMode.InitialMoney;
int initialMoney = CurrentSettings.InitialMoney;
if (subList.SelectedData is SubmarineInfo subInfo)
{
initialMoney -= subInfo.Price;
@@ -200,12 +203,16 @@ namespace Barotrauma
return TextManager.GetWithVariable("campaignstartingmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", initialMoney));
}
};
CampaignCustomizeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1f), firstPageButtonContainer.RectTransform, Anchor.CenterLeft), TextManager.Get("SettingsButton"))
{
OnClicked = (tb, userdata) =>
{
CreateCustomizeWindow();
CreateCustomizeWindow(CurrentSettings, settings =>
{
CurrentSettings = settings;
UpdateSubList(SubmarineInfo.SavedSubmarines);
});
return true;
}
};
@@ -218,7 +225,7 @@ namespace Barotrauma
return false;
}
};
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton")
{
IgnoreLayoutGroups = true,
@@ -353,54 +360,21 @@ namespace Barotrauma
StealRandomizeButton(CharacterMenus[i], jobTextContainer);
}
}
private void CreateCustomizeWindow()
private void CreateCustomizeWindow(CampaignSettings prevSettings, Action<CampaignSettings> onClosed = null)
{
CampaignCustomizeSettings = new GUIMessageBox("", "", new LocalizedString[] { TextManager.Get("OK") }, new Vector2(0.2f, 0.2f));
CampaignCustomizeSettings.Buttons[0].OnClicked += CampaignCustomizeSettings.Close;
CampaignCustomizeSettings = new GUIMessageBox("", "", new[] { TextManager.Get("OK") }, new Vector2(0.25f, 0.3f), minSize: new Point(450, 350));
CampaignSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter))
{
RelativeSpacing = 0.1f
};
GUILayoutGroup campaignSettingContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.8f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter));
if (MapGenerationParams.Instance.RadiationParams != null)
{
bool prevRadiationToggleEnabled = EnableRadiationToggle?.Selected ?? true;
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), CampaignSettingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUIStyle.Font)
{
Selected = prevRadiationToggleEnabled,
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
};
}
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), CampaignSettingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
ToolTip = TextManager.Get("maxmissioncounttooltip")
};
var maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", "missions"), wrap: true);
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
var maxMissionCountButtons = new GUIButton[2];
maxMissionCountButtons[0] = new GUIButton(new RectTransform(new Vector2(0.15f, 0.8f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleLeft")
{
OnClicked = (button, obj) =>
{
MaxMissionCountText.Text = Math.Clamp(Int32.Parse(MaxMissionCountText.Text.SanitizedValue) - 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString();
return true;
}
};
RichString prevMaxMissionCountText = MaxMissionCountText?.Text ?? CampaignSettings.DefaultMaxMissionCount.ToString();
MaxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), prevMaxMissionCountText, textAlignment: Alignment.Center, style: "GUITextBox");
maxMissionCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.15f, 0.8f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleRight")
CampaignSettingElements elements = CreateCampaignSettingList(campaignSettingContent, prevSettings);
CampaignCustomizeSettings.Buttons[0].OnClicked += (button, o) =>
{
OnClicked = (button, obj) =>
{
MaxMissionCountText.Text = Math.Clamp(Int32.Parse(MaxMissionCountText.Text.SanitizedValue) + 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString();
return true;
}
onClosed?.Invoke(elements.CreateSettings());
return CampaignCustomizeSettings.Close(button, o);
};
maxMissionCountContainer.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
}
private static void StealRandomizeButton(CharacterInfo.AppearanceCustomizationMenu menu, GUIComponent parent)
@@ -412,7 +386,7 @@ namespace Barotrauma
randomizeButton.RectTransform.Parent = parent.RectTransform;
randomizeButton.RectTransform.RelativeSize = Vector2.One * 1.3f;
}
private bool FinishSetup(GUIButton btn, object userdata)
{
if (string.IsNullOrWhiteSpace(saveNameBox.Text))
@@ -420,7 +394,7 @@ namespace Barotrauma
saveNameBox.Flash(GUIStyle.Red);
return false;
}
SubmarineInfo selectedSub = null;
if (!(subList.SelectedData is SubmarineInfo)) { return false; }
@@ -443,16 +417,7 @@ namespace Barotrauma
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Singleplayer, saveNameBox.Text);
bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled;
CampaignSettings settings = new CampaignSettings();
settings.RadiationEnabled = EnableRadiationToggle?.Selected ?? false;
if (MaxMissionCountText != null && Int32.TryParse(MaxMissionCountText.Text.SanitizedValue, out int missionCount))
{
settings.MaxMissionCount = missionCount;
}
else
{
settings.MaxMissionCount = CampaignSettings.DefaultMaxMissionCount;
}
CampaignSettings settings = CurrentSettings;
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
{
@@ -499,7 +464,7 @@ namespace Barotrauma
return true;
}
public void RandomizeSeed()
{
seedBox.Text = ToolBox.RandomSeed(8);
@@ -509,7 +474,7 @@ namespace Barotrauma
{
foreach (GUIComponent child in subList.Content.Children)
{
var sub = child.UserData as SubmarineInfo;
SubmarineInfo sub = child.UserData as SubmarineInfo;
if (sub == null) { return; }
child.Visible = string.IsNullOrEmpty(filter) || sub.DisplayName.Contains(filter.ToLower(), StringComparison.OrdinalIgnoreCase);
}
@@ -523,7 +488,7 @@ namespace Barotrauma
if (!(obj is SubmarineInfo sub)) { return true; }
#if !DEBUG
if (sub.Price > CampaignMode.InitialMoney && !GameMain.DebugDraw)
if (sub.Price > CurrentSettings.InitialMoney && !GameMain.DebugDraw)
{
SetPage(0);
nextButton.Enabled = false;
@@ -556,8 +521,8 @@ namespace Barotrauma
subsToShow.Sort((s1, s2) =>
{
int p1 = s1.Price > CampaignMode.InitialMoney ? 10 : 0;
int p2 = s2.Price > CampaignMode.InitialMoney ? 10 : 0;
int p1 = s1.Price > CurrentSettings.InitialMoney ? 10 : 0;
int p2 = s2.Price > CurrentSettings.InitialMoney ? 10 : 0;
return p1.CompareTo(p2) * 100 + s1.Name.CompareTo(s2.Name);
});
@@ -582,13 +547,13 @@ namespace Barotrauma
var priceText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), textBlock.RectTransform, Anchor.CenterRight),
TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", sub.Price)), textAlignment: Alignment.CenterRight, font: GUIStyle.SmallFont)
{
TextColor = sub.Price > CampaignMode.InitialMoney ? GUIStyle.Red : textBlock.TextColor * 0.8f,
TextColor = sub.Price > CurrentSettings.InitialMoney ? GUIStyle.Red : textBlock.TextColor * 0.8f,
ToolTip = textBlock.ToolTip
};
#if !DEBUG
if (!GameMain.DebugDraw)
{
if (sub.Price > CampaignMode.InitialMoney || !sub.IsCampaignCompatible)
if (sub.Price > CurrentSettings.InitialMoney || !sub.IsCampaignCompatible)
{
textBlock.CanBeFocused = false;
textBlock.TextColor *= 0.5f;
@@ -598,7 +563,7 @@ namespace Barotrauma
}
if (SubmarineInfo.SavedSubmarines.Any())
{
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.InitialMoney).ToList();
var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CurrentSettings.InitialMoney).ToList();
if (validSubs.Count > 0)
{
subList.Select(validSubs[Rand.Int(validSubs.Count)]);
@@ -625,6 +590,7 @@ namespace Barotrauma
saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = SelectSaveFile
};
@@ -650,8 +616,9 @@ namespace Barotrauma
{
var saveFrame = CreateSaveElement(saveInfo);
if (saveFrame == null) { continue; }
XDocument doc = SaveUtil.LoadGameSessionDoc(saveInfo.FilePath);
if (doc?.Root == null)
{
DebugConsole.ThrowError("Error loading save file \"" + saveInfo.FilePath + "\". The file may be corrupted.");
@@ -725,9 +692,10 @@ namespace Barotrauma
string subName = doc.Root.GetAttributeString("submarine", "");
string saveTime = doc.Root.GetAttributeString("savetime", "unknown");
DateTime? time = null;
if (long.TryParse(saveTime, out long unixTime))
{
DateTime time = ToolBox.Epoch.ToDateTime(unixTime);
time = ToolBox.Epoch.ToDateTime(unixTime);
saveTime = time.ToString();
}
@@ -729,7 +729,7 @@ namespace Barotrauma
break;
case CampaignMode.InteractionType.PurchaseSub:
if (submarineSelection == null) submarineSelection = new SubmarineSelection(false, () => Campaign.ShowCampaignUI = false, tabs[(int)CampaignMode.InteractionType.PurchaseSub].RectTransform);
submarineSelection.RefreshSubmarineDisplay(true);
submarineSelection.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
break;
}
}
@@ -2858,7 +2858,10 @@ namespace Barotrauma.CharacterEditor
{
var loadBox = new GUIMessageBox(GetCharacterEditorTranslation("LoadRagdoll"), "", new LocalizedString[] { TextManager.Get("Cancel"), TextManager.Get("Load"), TextManager.Get("Delete") }, messageBoxRelSize);
loadBox.Buttons[0].OnClicked += loadBox.Close;
var listBox = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform, Anchor.TopCenter));
var listBox = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform, Anchor.TopCenter))
{
PlaySoundOnSelect = true,
};
var deleteButton = loadBox.Buttons[2];
deleteButton.Enabled = false;
void PopulateListBox()
@@ -2996,7 +2999,10 @@ namespace Barotrauma.CharacterEditor
{
var loadBox = new GUIMessageBox(GetCharacterEditorTranslation("LoadAnimation"), "", new LocalizedString[] { TextManager.Get("Cancel"), TextManager.Get("Load"), TextManager.Get("Delete") }, messageBoxRelSize);
loadBox.Buttons[0].OnClicked += loadBox.Close;
var listBox = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform));
var listBox = new GUIListBox(new RectTransform(new Vector2(0.9f, 0.6f), loadBox.Content.RectTransform))
{
PlaySoundOnSelect = true,
};
var deleteButton = loadBox.Buttons[2];
deleteButton.Enabled = false;
// Type filtering
@@ -482,7 +482,10 @@ namespace Barotrauma.CharacterEditor
RelativeSpacing = 0.02f
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), limbEditLayout.RectTransform), GetCharacterEditorTranslation("Limbs"), font: GUIStyle.SubHeadingFont);
var limbsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), content.RectTransform));
var limbsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), content.RectTransform))
{
PlaySoundOnSelect = true,
};
var removeLimbButton = new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), limbEditLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton")
{
OnClicked = (b, d) =>
@@ -659,7 +662,10 @@ namespace Barotrauma.CharacterEditor
{
CanBeFocused = false
};
var jointsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), content.RectTransform));
var jointsList = new GUIListBox(new RectTransform(new Vector2(1, 0.45f), content.RectTransform))
{
PlaySoundOnSelect = true,
};
var removeJointButton = new GUIButton(new RectTransform(new Point(jointButtonElement.Rect.Height, jointButtonElement.Rect.Height), jointButtonElement.RectTransform), style: "GUIMinusButton")
{
OnClicked = (b, d) =>
@@ -225,7 +225,7 @@ namespace Barotrauma
return true;
}
public static GUIMessageBox AskForConfirmation(LocalizedString header, LocalizedString body, Func<bool> onConfirm)
public static GUIMessageBox AskForConfirmation(LocalizedString header, LocalizedString body, Func<bool> onConfirm, GUISoundType? overrideConfirmButtonSound = null)
{
LocalizedString[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons);
@@ -244,6 +244,10 @@ namespace Barotrauma
msgBox.Close();
return true;
};
if (overrideConfirmButtonSound.HasValue)
{
msgBox.Buttons[0].ClickSound = overrideConfirmButtonSound.Value;
}
return msgBox;
}
@@ -34,6 +34,8 @@ namespace Barotrauma
private readonly GUITickBox lightingEnabled, cursorLightEnabled, allowInvalidOutpost, mirrorLevel;
private readonly GUIDropDown selectedSubDropDown;
private Sprite editingSprite;
private LightSource pointerLightSource;
@@ -57,7 +59,10 @@ namespace Barotrauma
RelativeSpacing = 0.01f
};
paramsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), paddedLeftPanel.RectTransform));
paramsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true
};
paramsList.OnSelected += (GUIComponent component, object obj) =>
{
selectedParams = obj as LevelGenerationParams;
@@ -70,7 +75,10 @@ namespace Barotrauma
var ruinTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.ruinparams"), font: GUIStyle.SubHeadingFont);
ruinParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
ruinParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true
};
ruinParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
@@ -79,7 +87,10 @@ namespace Barotrauma
var caveTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.caveparams"), font: GUIStyle.SubHeadingFont);
caveParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform));
caveParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.1f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true
};
caveParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateCaveParamsEditor(obj as CaveGenerationParams);
@@ -89,7 +100,10 @@ namespace Barotrauma
var outpostTitle = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedLeftPanel.RectTransform), TextManager.Get("leveleditor.outpostparams"), font: GUIStyle.SubHeadingFont);
GUITextBlock.AutoScaleAndNormalize(ruinTitle, caveTitle, outpostTitle);
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform));
outpostParamsList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true
};
outpostParamsList.OnSelected += (GUIComponent component, object obj) =>
{
CreateOutpostGenerationParamsEditor(obj as OutpostGenerationParams);
@@ -171,6 +185,16 @@ namespace Barotrauma
Vector2 GetSeedElementRelativeSize() => new Vector2(0.5f * (1.0f - randomizeButtonRelativeSize.X), 1.0f);
static string GetLevelSeed() => ToolBox.RandomSeed(8);
var subDropDownContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), subDropDownContainer.RectTransform), TextManager.Get("submarine"));
selectedSubDropDown = new GUIDropDown(new RectTransform(new Vector2(0.5f, 1.0f), subDropDownContainer.RectTransform));
foreach (SubmarineInfo sub in SubmarineInfo.SavedSubmarines)
{
if (sub.Type != SubmarineType.Player) { continue; }
selectedSubDropDown.AddItem(sub.DisplayName, userData: sub);
}
subDropDownContainer.RectTransform.MinSize = new Point(0, selectedSubDropDown.RectTransform.MinSize.Y);
mirrorLevel = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.02f), paddedRightPanel.RectTransform), TextManager.Get("mirrorentityx"));
allowInvalidOutpost = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.025f), paddedRightPanel.RectTransform),
@@ -186,11 +210,18 @@ namespace Barotrauma
{
bool wasLevelLoaded = Level.Loaded != null;
Submarine.Unload();
if (selectedSubDropDown.SelectedData is SubmarineInfo subInfo)
{
Submarine.MainSub = new Submarine(subInfo);
}
GameMain.LightManager.ClearLights();
currentLevelData = LevelData.CreateRandom(seedBox.Text, generationParams: selectedParams);
currentLevelData.ForceOutpostGenerationParams = outpostParamsList.SelectedData as OutpostGenerationParams;
currentLevelData.AllowInvalidOutpost = allowInvalidOutpost.Selected;
Level.Generate(currentLevelData, mirror: mirrorLevel.Selected);
var dummyLocations = GameSession.CreateDummyLocations(seed: currentLevelData.Seed);
Level.Generate(currentLevelData, mirror: mirrorLevel.Selected, startLocation: dummyLocations[0], endLocation: dummyLocations[1]);
Submarine.MainSub?.SetPosition(Level.Loaded.StartPosition);
GameMain.LightManager.AddLight(pointerLightSource);
if (!wasLevelLoaded || Cam.Position.X < 0 || Cam.Position.Y < 0 || Cam.Position.Y > Level.Loaded.Size.X || Cam.Position.Y > Level.Loaded.Size.Y)
{
@@ -228,7 +259,7 @@ namespace Barotrauma
var nonPlayerFiles = ContentPackageManager.EnabledPackages.All.SelectMany(p => p
.GetFiles<BaseSubFile>()
.Where(f => !(f is SubmarineFile))).ToArray();
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == GameSettings.CurrentConfig.QuickStartSub);
SubmarineInfo subInfo = selectedSubDropDown.SelectedData as SubmarineInfo;
subInfo ??= SubmarineInfo.SavedSubmarines.GetRandomUnsynced(s =>
s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) &&
!nonPlayerFiles.Any(f => f.Path == s.FilePath));
@@ -259,6 +290,7 @@ namespace Barotrauma
levelObjectList = new GUIListBox(new RectTransform(new Vector2(0.99f, 0.85f), bottomPanel.RectTransform, Anchor.Center))
{
PlaySoundOnSelect = true,
UseGridLayout = true
};
levelObjectList.OnSelected += (GUIComponent component, object obj) =>
@@ -866,7 +898,11 @@ namespace Barotrauma
{
foreach (Item item in Item.ItemList)
{
item?.GetComponent<Items.Components.LightComponent>()?.Update((float)deltaTime, Cam);
if (item == null) { continue; }
foreach (var light in item.GetComponents<Items.Components.LightComponent>())
{
light.Update((float)deltaTime, Cam);
}
}
}
GameMain.LightManager?.Update((float)deltaTime);
@@ -429,7 +429,10 @@ namespace Barotrauma
//PLACEHOLDER
var tutorialList = new GUIListBox(
new RectTransform(new Vector2(0.95f, 0.85f), menuTabs[Tab.Tutorials].RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.1f) });
new RectTransform(new Vector2(0.95f, 0.85f), menuTabs[Tab.Tutorials].RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.1f) })
{
PlaySoundOnSelect = true,
};
var tutorialTypes = new List<Type>()
{
typeof(MechanicTutorial),
@@ -850,16 +853,12 @@ namespace Barotrauma
arguments += " -nopassword";
}
int ownerKey = 0;
if (Steam.SteamManager.GetSteamID() != 0)
{
arguments += " -steamid " + Steam.SteamManager.GetSteamID();
}
else
{
ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
arguments += " -ownerkey " + ownerKey;
}
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
arguments += " -ownerkey " + ownerKey;
string filename = Path.Combine(
Path.GetDirectoryName(exeName),
@@ -1244,7 +1243,8 @@ namespace Barotrauma
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton", textAlignment: Alignment.Center)
{
UserData = -1,
OnClicked = ChangeMaxPlayers
OnClicked = ChangeMaxPlayers,
ClickSound = GUISoundType.Decrease
};
maxPlayersBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center)
{
@@ -1264,7 +1264,8 @@ namespace Barotrauma
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center)
{
UserData = 1,
OnClicked = ChangeMaxPlayers
OnClicked = ChangeMaxPlayers,
ClickSound = GUISoundType.Increase
};
maxPlayersLabel.RectTransform.IsFixedSize = true;
@@ -179,7 +179,7 @@ namespace Barotrauma
get { return ModeList.SelectedIndex; }
set
{
ModeList.Select(value, true);
ModeList.Select(value, GUIListBox.Force.Yes);
}
}
@@ -504,6 +504,7 @@ namespace Barotrauma
PlayerList = new GUIListBox(new RectTransform(new Vector2(0.4f, 1.0f), socialHolderHorizontal.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (component, userdata) => { SelectPlayer(userdata as Client); return true; }
};
@@ -816,6 +817,7 @@ namespace Barotrauma
SubList = new GUIListBox(new RectTransform(Vector2.One, subHolder.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = VotableClicked
};
@@ -901,6 +903,7 @@ namespace Barotrauma
};
ModeList = new GUIListBox(new RectTransform(Vector2.One, gameModeHolder.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = VotableClicked
};
@@ -1515,6 +1518,7 @@ namespace Barotrauma
JobList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), JobPreferenceContainer.RectTransform, Anchor.BottomCenter), true)
{
Enabled = true,
PlaySoundOnSelect = true,
OnSelected = (child, obj) =>
{
if (child.IsParentOf(GUI.MouseOn)) return false;
@@ -1600,6 +1604,7 @@ namespace Barotrauma
{
Enabled = true,
KeepSpaceForScrollBar = false,
PlaySoundOnSelect = true,
ScrollBarEnabled = false,
ScrollBarVisible = false
};
@@ -3185,7 +3190,7 @@ namespace Barotrauma
var prevMode = ModeList.Content.GetChild(selectedModeIndex).UserData as GameModePreset;
if ((HighlightedModeIndex == selectedModeIndex || HighlightedModeIndex < 0) && ModeList.SelectedIndex != modeIndex) { ModeList.Select(modeIndex, true); }
if ((HighlightedModeIndex == selectedModeIndex || HighlightedModeIndex < 0) && ModeList.SelectedIndex != modeIndex) { ModeList.Select(modeIndex, GUIListBox.Force.Yes); }
selectedModeIndex = modeIndex;
if ((prevMode == GameModePreset.PvP) != (SelectedMode == GameModePreset.PvP))
@@ -3301,7 +3306,7 @@ namespace Barotrauma
RefreshEnabledElements();
if (enabled)
{
ModeList.Select(GameModePreset.MultiPlayerCampaign, true);
ModeList.Select(GameModePreset.MultiPlayerCampaign, GUIListBox.Force.Yes);
}
}
@@ -3417,7 +3422,7 @@ namespace Barotrauma
UserData = i,
OnClicked = (btn, obj) =>
{
JobList.Select((int)obj, true);
JobList.Select((int)obj, GUIListBox.Force.Yes);
SwitchJob(btn, null);
if (JobSelectionFrame != null) { JobSelectionFrame.Visible = false; }
JobList.Deselect();
@@ -3553,7 +3558,7 @@ namespace Barotrauma
else
{
subList.OnSelected -= VotableClicked;
subList.Select(sub, force: true);
subList.Select(sub, GUIListBox.Force.Yes);
subList.OnSelected += VotableClicked;
}
@@ -129,7 +129,10 @@ namespace Barotrauma
OnClicked = (btn, userdata) => { FilterEmitters(""); filterBox.Text = ""; filterBox.Flash(Color.White); return true; }
};
prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform));
prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true,
};
prefabList.OnSelected += (GUIComponent component, object obj) =>
{
cam.Position = Vector2.Zero;
@@ -504,6 +504,7 @@ namespace Barotrauma
serverList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListContainer.RectTransform, Anchor.Center))
{
PlaySoundOnSelect = true,
ScrollBarVisible = true,
OnSelected = (btn, obj) =>
{
@@ -1473,7 +1474,6 @@ namespace Barotrauma
{
friendsDropdownButton = new GUIButton(new RectTransform(Vector2.One, friendsButtonHolder.RectTransform, Anchor.BottomRight, Pivot.BottomRight, scaleBasis: ScaleBasis.BothHeight), "\u2022 \u2022 \u2022", style: "GUIButtonFriendsDropdown")
{
Font = GUIStyle.GlobalFont,
OnClicked = (button, udt) =>
{
friendsDropdown.RectTransform.NonScaledSize = new Point(friendsButtonHolder.Rect.Height * 5 * 166 / 100, friendsButtonHolder.Rect.Height * 4 * 166 / 100);
@@ -85,12 +85,12 @@ namespace Barotrauma
{
OnClicked = (button, userData) =>
{
var selected = selectedSprites;
var selected = selectedSprites.ToList();
Sprite firstSelected = selected.First();
selected.ForEach(s => s.ReloadTexture());
RefreshLists();
textureList.Select(firstSelected.FullPath, autoScroll: false);
selected.ForEachMod(s => spriteList.Select(s, autoScroll: false));
textureList.Select(firstSelected.FullPath, autoScroll: GUIListBox.AutoScroll.Disabled);
selected.ForEachMod(s => spriteList.Select(s, autoScroll: GUIListBox.AutoScroll.Disabled));
texturePathText.Text = TextManager.GetWithVariable("spriteeditor.texturesreloaded", "[filepath]", firstSelected.FilePath.Value);
texturePathText.TextColor = GUIStyle.Green;
return true;
@@ -206,6 +206,7 @@ namespace Barotrauma
textureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedLeftPanel.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (listBox, userData) =>
{
var newTexturePath = userData as string;
@@ -213,7 +214,7 @@ namespace Barotrauma
{
selectedTexturePath = newTexturePath;
ResetZoom();
spriteList.Select(loadedSprites.First(s => s.FilePath == selectedTexturePath), autoScroll: false);
spriteList.Select(loadedSprites.First(s => s.FilePath == selectedTexturePath), autoScroll: GUIListBox.AutoScroll.Disabled);
UpdateScrollBar(spriteList);
}
foreach (GUIComponent child in spriteList.Content.Children)
@@ -248,6 +249,7 @@ namespace Barotrauma
spriteList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform))
{
PlaySoundOnSelect = true,
OnSelected = (listBox, userData) =>
{
if (userData is Sprite sprite)
@@ -481,7 +483,7 @@ namespace Barotrauma
var scaledRect = new Rectangle(textureRect.Location + sprite.SourceRect.Location.Multiply(zoom), sprite.SourceRect.Size.Multiply(zoom));
if (scaledRect.Contains(PlayerInput.MousePosition))
{
spriteList.Select(sprite, autoScroll: false);
spriteList.Select(sprite, autoScroll: GUIListBox.AutoScroll.Disabled);
UpdateScrollBar(spriteList);
UpdateScrollBar(textureList);
// Release the keyboard so that we can nudge the source rects
@@ -847,7 +849,7 @@ namespace Barotrauma
base.Select();
LoadSprites();
RefreshLists();
spriteList.Select(0, autoScroll: false);
spriteList.Select(0, autoScroll: GUIListBox.AutoScroll.Disabled);
}
protected override void DeselectEditorSpecific()
@@ -905,7 +907,7 @@ namespace Barotrauma
}
if (sprite.FullPath != selectedTexturePath)
{
textureList.Select(sprite.FullPath, autoScroll: false);
textureList.Select(sprite.FullPath, autoScroll: GUIListBox.AutoScroll.Disabled);
UpdateScrollBar(textureList);
}
xmlPathText.Text = string.Empty;
File diff suppressed because it is too large Load Diff
@@ -1282,6 +1282,7 @@ namespace Barotrauma
var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter))
{
PlaySoundOnSelect = true,
OnSelected = (component, userData) =>
{
string text = userData as string ?? "";
@@ -171,7 +171,7 @@ namespace Barotrauma
int childIndex = values.IndexOf(currentValue);
dropdown.Select(childIndex);
dropdown.ListBox.ForceLayoutRecalculation();
dropdown.ListBox.ScrollToElement(dropdown.ListBox.Content.GetChild(childIndex), playSound: false);
dropdown.ListBox.ScrollToElement(dropdown.ListBox.Content.GetChild(childIndex));
dropdown.OnSelected = (dd, obj) =>
{
setter((T)obj);
@@ -418,7 +418,7 @@ namespace Barotrauma
}
else
{
if (!Level.IsLoadedOutpost && Character.Controlled?.CurrentHull?.Submarine is Submarine sub &&
if (!Level.IsLoadedFriendlyOutpost && Character.Controlled?.CurrentHull?.Submarine is Submarine sub &&
sub.Info != null && !sub.Info.IsOutpost)
{
hullSoundSource = Character.Controlled.CurrentHull;
@@ -889,5 +889,13 @@ namespace Barotrauma
.Where(s => s.Type == soundType)
.GetRandomUnsynced()?.Sound?.Play(null, "ui");
}
public static void PlayUISound(GUISoundType? soundType)
{
if (soundType.HasValue)
{
PlayUISound(soundType.Value);
}
}
}
}
@@ -176,12 +176,15 @@ namespace Barotrauma.Steam
Directory.CreateDirectory(PublishStagingDir);
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir, ShouldCorrectPaths.No);
var stagingFileListPath = Path.Combine(PublishStagingDir, ContentPackage.FileListFileName);
ContentPackage tempPkg = ContentPackage.TryLoad(stagingFileListPath) ?? throw new Exception("Staging copy could not be loaded");
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
ModProject modProject = new ModProject(contentPackage)
ModProject modProject = new ModProject(tempPkg)
{
ModVersion = modVersion
};
modProject.Save(Path.Combine(PublishStagingDir, ContentPackage.FileListFileName));
modProject.Save(stagingFileListPath);
}
public static async Task<ContentPackage?> CreateLocalCopy(ContentPackage contentPackage)
@@ -46,7 +46,10 @@ namespace Barotrauma.Steam
regularBox.CanBeFocused = true;
}
}
filterBox = CreateSearchBox(mainLayout, width: 1.0f);
var searchRectT = NewItemRectT(mainLayout, heightScale: 1.0f);
searchRectT.RelativeSize = (1.0f, searchRectT.RelativeSize.Y);
filterBox = CreateSearchBox(searchRectT);
Label(mainLayout, TextManager.Get("CannotChangeMods"), GUIStyle.Font);
}
@@ -55,9 +58,8 @@ namespace Barotrauma.Steam
{
string str = filterBox.Text;
regularList.Content.Children
.ForEach(c => c.Visible = str.IsNullOrWhiteSpace()
|| (c.UserData is ContentPackage p
&& p.Name.Contains(str, StringComparison.OrdinalIgnoreCase)));
.ForEach(c => c.Visible = !(c.UserData is ContentPackage p)
|| ModNameMatches(p, str));
}
}
}
@@ -0,0 +1,746 @@
#nullable enable
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
namespace Barotrauma.Steam
{
sealed partial class MutableWorkshopMenu : WorkshopMenu
{
private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
private readonly GUIDropDown enabledCoreDropdown;
private readonly GUIListBox enabledRegularModsList;
private readonly GUIListBox disabledRegularModsList;
private readonly Action<ItemOrPackage> onInstalledInfoButtonHit;
private readonly GUITextBox modsListFilter;
private readonly Dictionary<Filter, GUITickBox> modsListFilterTickboxes;
private readonly GUIButton bulkUpdateButton;
private GUIComponent? draggedElement = null;
private GUIListBox? draggedElementOrigin = null;
private void UpdateSubscribedModInstalls()
{
if (!SteamManager.IsInitialized) { return; }
uint numSubscribedMods = SteamManager.GetNumSubscribedItems();
if (numSubscribedMods == memSubscribedModCount) { return; }
memSubscribedModCount = numSubscribedMods;
var subscribedIds = SteamManager.GetSubscribedItems().ToHashSet();
var installedIds = ContentPackageManager.WorkshopPackages.Select(p => p.SteamWorkshopId).ToHashSet();
foreach (var id in subscribedIds.Where(id2 => !installedIds.Contains(id2)))
{
Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id);
if (!item.IsDownloading && !SteamManager.Workshop.IsInstalling(item))
{
SteamManager.Workshop.DownloadModThenEnqueueInstall(item);
}
}
TaskPool.Add("RemoveUnsubscribedItems", SteamManager.Workshop.GetPublishedItems(), t =>
{
if (!t.TryGetResult(out ISet<Steamworks.Ugc.Item> publishedItems)) { return; }
var allRequiredInstalled = subscribedIds.Union(publishedItems.Select(it => it.Id)).ToHashSet();
bool needsRefresh = false;
foreach (var id in installedIds.Where(id2 => !allRequiredInstalled.Contains(id2)))
{
Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id);
SteamManager.Workshop.Uninstall(item);
needsRefresh = true;
}
if (needsRefresh)
{
PopulateInstalledModLists();
}
});
}
private static (GUILayoutGroup Left, GUIFrame center, GUILayoutGroup Right) CreateSidebars(
GUIComponent parent,
float leftWidth = 0.3875f,
float centerWidth = 0.025f,
float rightWidth = 0.5875f,
bool split = false,
float height = 1.0f)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform((1.0f, height), parent.RectTransform), isHorizontal: true);
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((leftWidth, 1.0f), layout.RectTransform), isHorizontal: false);
var center = new GUIFrame(new RectTransform((centerWidth, 1.0f), layout.RectTransform), style: null);
if (split)
{
new GUICustomComponent(new RectTransform(Vector2.One, center.RectTransform),
onDraw: (sb, c) =>
{
sb.DrawLine((c.Rect.Center.X, c.Rect.Top), (c.Rect.Center.X, c.Rect.Bottom), GUIStyle.TextColorDim, 2f);
});
}
GUILayoutGroup right = new GUILayoutGroup(new RectTransform((rightWidth, 1.0f), layout.RectTransform), isHorizontal: false);
return (left, center, right);
}
private void HandleDraggingAcrossModLists(GUIListBox from, GUIListBox to)
{
if (to.Rect.Contains(PlayerInput.MousePosition) && from.DraggedElement != null)
{
//move the dragged elements to the index determined previously
var draggedElement = from.DraggedElement;
var selected = from.AllSelected.ToList();
selected.Sort((a, b) => from.Content.GetChildIndex(a) - from.Content.GetChildIndex(b));
float oldCount = to.Content.CountChildren;
float newCount = oldCount + selected.Count;
var offset = draggedElement.RectTransform.AbsoluteOffset;
offset += from.Content.Rect.Location;
offset -= to.Content.Rect.Location;
for (int i = 0; i < selected.Count; i++)
{
var c = selected[i];
c.Parent.RemoveChild(c);
c.RectTransform.Parent = to.Content.RectTransform;
c.RectTransform.RepositionChildInHierarchy((int)oldCount+i);
}
from.DraggedElement = null;
from.Deselect();
from.RecalculateChildren();
from.RectTransform.RecalculateScale(true);
to.RecalculateChildren();
to.RectTransform.RecalculateScale(true);
to.Select(selected);
//recalculate the dragged element's offset so it doesn't jump around
draggedElement.RectTransform.AbsoluteOffset = offset;
to.DraggedElement = draggedElement;
to.BarScroll *= (oldCount / newCount);
}
}
private Action? currentSwapFunc = null;
private GUISoundType? swapSoundType = null;
private void PlaySwapSound()
{
SoundPlayer.PlayUISound(swapSoundType);
}
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);
};
if (to == enabledRegularModsList)
{
swapSoundType = GUISoundType.Increase;
}
else if (to == disabledRegularModsList)
{
swapSoundType = GUISoundType.Decrease;
}
else
{
swapSoundType = null;
}
}
private void CreateInstalledModsTab(
out GUIDropDown enabledCoreDropdown,
out GUIListBox enabledRegularModsList,
out GUIListBox disabledRegularModsList,
out Action<ItemOrPackage> onInstalledInfoButtonHit,
out GUITextBox modsListFilter,
out Dictionary<Filter, GUITickBox> modsListFilterTickboxes,
out GUIButton bulkUpdateButton)
{
GUIFrame content = CreateNewContentFrame(Tab.InstalledMods);
CreateWorkshopItemDetailContainer(
content,
out var outerContainer,
onSelected: (itemOrPackage, selectedFrame) =>
{
if (itemOrPackage.TryGet(out Steamworks.Ugc.Item item)) { PopulateFrameWithItemInfo(item, selectedFrame); }
},
onDeselected: () => PopulateInstalledModLists(),
out onInstalledInfoButtonHit, out var deselect);
GUILayoutGroup mainLayout =
new GUILayoutGroup(new RectTransform(Vector2.One, outerContainer.Content.RectTransform), childAnchor: Anchor.TopCenter);
mainLayout.RectTransform.SetAsFirstChild();
var (topLeft, _, topRight) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.13f);
topLeft.Stretch = true;
Label(topLeft, TextManager.Get("enabledcore"), GUIStyle.SubHeadingFont, heightScale: 1.0f);
enabledCoreDropdown = Dropdown<CorePackage>(topLeft,
(p) => p.Name,
ContentPackageManager.CorePackages.ToArray(),
ContentPackageManager.EnabledPackages.Core!,
(p) => { },
heightScale: 1.0f / 13.0f);
Label(topLeft, "", GUIStyle.SubHeadingFont, heightScale: 1.0f);
topRight.ChildAnchor = Anchor.CenterLeft;
var topRightButtons = new GUILayoutGroup(new RectTransform((1.0f, 0.5f), topRight.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.05f
};
void padTopRight(float width=1.0f)
{
new GUIFrame(new RectTransform((width, 1.0f), topRightButtons.RectTransform), style: null);
}
padTopRight();
//TODO: put stuff here
padTopRight(width: 3.0f);
var refreshListsButton
= new GUIButton(
new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight),
text: "", style: "GUIReloadButton")
{
OnClicked = (b, o) =>
{
PopulateInstalledModLists();
return false;
},
ToolTip = TextManager.Get("RefreshModLists")
};
bulkUpdateButton
= new GUIButton(
new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight),
text: "", style: "GUIUpdateButton")
{
OnClicked = (b, o) =>
{
BulkDownloader.PrepareUpdates();
return false;
},
Enabled = false
};
padTopRight(width: 0.1f);
var (left, center, right) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.8f);
right.ChildAnchor = Anchor.TopRight;
//enabled mods
Label(left, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont);
var enabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), left.RectTransform))
{
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple,
HideDraggedElement = true,
PlaySoundOnSelect = true,
SoundOnDragStart = GUISoundType.Select,
SoundOnDragStop = GUISoundType.Increase,
};
enabledRegularModsList = enabledModsList;
//disabled mods
Label(right, TextManager.Get("disabledregular"), GUIStyle.SubHeadingFont);
var disabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), right.RectTransform))
{
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple,
HideDraggedElement = true,
PlaySoundOnSelect = true,
SoundOnDragStart = GUISoundType.Select,
SoundOnDragStop = GUISoundType.Decrease,
};
disabledRegularModsList = disabledModsList;
var centerButton =
new GUIButton(
new RectTransform(Vector2.One * 0.95f, center.RectTransform, scaleBasis: ScaleBasis.BothWidth,
anchor: Anchor.Center),
style: "GUIButtonToggleLeft")
{
PlaySoundOnSelect = false,
Visible = false,
OnClicked = (button, o) =>
{
if (currentSwapFunc != null)
{
PlaySwapSound();
currentSwapFunc.Invoke();
}
return false;
}
};
enabledModsList.OnSelected = (frame, o) =>
{
disabledModsList.Deselect();
centerButton.Visible = true;
centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleRight"));
SetSwapFunc(enabledModsList, disabledModsList);
return true;
};
disabledModsList.OnSelected = (frame, o) =>
{
enabledModsList.Deselect();
centerButton.Visible = true;
centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleLeft"));
SetSwapFunc(disabledModsList, enabledModsList);
return true;
};
var filterContainer = new GUILayoutGroup(NewItemRectT(mainLayout, heightScale: 1.0f), isHorizontal: true)
{ Stretch = true, RelativeSpacing = 0.01f };
void padFilterContainer(float width = 0.25f)
=> new GUIFrame(new RectTransform((width, 1.0f), filterContainer!.RectTransform), style: null);
GUIButton filterLayoutButton(string style)
=> new GUIButton(
new RectTransform(Vector2.One, filterContainer!.RectTransform, scaleBasis: ScaleBasis.BothHeight),
"", style: style);
padFilterContainer(width: 0.2f);
var loadPresetBtn = filterLayoutButton("OpenButton");
loadPresetBtn.ToolTip = TextManager.Get("LoadModListPresetHeader");
loadPresetBtn.OnClicked = OpenLoadPreset;
var savePresetBtn = filterLayoutButton("SaveButton");
savePresetBtn.ToolTip = TextManager.Get("SaveModListPresetHeader");
savePresetBtn.OnClicked = OpenSavePreset;
padFilterContainer(width: 0.05f);
var searchRectT = new RectTransform((0.5f, 1.0f), filterContainer.RectTransform);
var searchBox = CreateSearchBox(searchRectT);
modsListFilter = searchBox;
var filterTickboxes = new Dictionary<Filter, GUITickBox>();
modsListFilterTickboxes = filterTickboxes;
var filterTickboxesDropdown
= filterLayoutButton("SetupVisibilityButton");
var filterTickboxesContainer
= new GUIFrame(new RectTransform((0.3f, 0.2f), content.RectTransform,
scaleBasis: ScaleBasis.BothWidth), style: "InnerFrame");
var filterTickboxesUpdater
= new GUICustomComponent(new RectTransform(Vector2.Zero, content.RectTransform),
onUpdate: (f, component) =>
{
filterTickboxesContainer.Visible = filterTickboxesDropdown.Selected;
filterTickboxesContainer.RectTransform.AbsoluteOffset
= (filterTickboxesDropdown.Rect.Location - content.Rect.Location)
+ (filterTickboxesDropdown.Rect.Width / 2, 0)
- (filterTickboxesContainer.Rect.Size.ToVector2() * (0.5f, 1.0f)).ToPoint();
filterTickboxesContainer.RectTransform.NonScaledSize
= new Point(filterTickboxes.Select(tb => (int)tb.Value.Font.MeasureString(tb.Value.GetChild<GUITextBlock>().Text).X).Max(),
filterTickboxes.Select(tb => tb.Value.Rect.Height).Aggregate((a,b) => a+b))
+(filterTickboxes.Values.First().Rect.Height * 4, filterTickboxes.Values.First().Rect.Height / 2);
if (PlayerInput.PrimaryMouseButtonClicked()
&& !GUI.IsMouseOn(filterTickboxesDropdown)
&& !GUI.IsMouseOn(filterTickboxesContainer))
{
filterTickboxesDropdown.Selected = false;
}
});
var filterTickboxesLayout
= new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, filterTickboxesContainer.RectTransform, Anchor.Center));
void addFilterTickbox(Filter filter, string? style, bool selected)
{
var tickbox = new GUITickBox(NewItemRectT(filterTickboxesLayout!, heightScale: 0.5f), "")
{
Selected = selected,
OnSelected = _ =>
{
UpdateModListItemVisibility();
return true;
}
};
filterTickboxes!.Add(filter, tickbox);
var text = new GUITextBlock(new RectTransform((1.0f, 1.0f), tickbox.RectTransform, Anchor.CenterRight)
{
AbsoluteOffset = (-tickbox.Box.Rect.Width * 2, 0),
},
TextManager.Get($"ModFilter.{filter}"))
{
CanBeFocused = false
};
var icon = new GUIFrame(
new RectTransform(Vector2.One, text.RectTransform, Anchor.CenterLeft, Pivot.CenterRight,
scaleBasis: ScaleBasis.BothHeight), style: style)
{
CanBeFocused = false
};
}
addFilterTickbox(Filter.ShowLocal, "WorkshopMenu.EditButton", selected: true);
addFilterTickbox(Filter.ShowWorkshop, "WorkshopMenu.DownloadedIcon", selected: true);
addFilterTickbox(Filter.ShowPublished, "WorkshopMenu.PublishedIcon", selected: true);
addFilterTickbox(Filter.ShowOnlySubs, null, selected: false);
addFilterTickbox(Filter.ShowOnlyItemAssemblies, null, selected: false);
padFilterContainer();
new GUICustomComponent(new RectTransform(Vector2.Zero, content.RectTransform),
onUpdate: (f, component) =>
{
HandleDraggingAcrossModLists(enabledModsList, disabledModsList);
HandleDraggingAcrossModLists(disabledModsList, enabledModsList);
UpdateDraggingSounds();
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) =>
{
enabledModsList.DraggedElement?.DrawManually(spriteBatch, true, true);
disabledModsList.DraggedElement?.DrawManually(spriteBatch, true, true);
});
void UpdateDraggingSounds()
{
if (draggedElement != null)
{
if (enabledModsList.DraggedElement == null && disabledModsList.DraggedElement == null)
{
SetDragOrigin(null);
}
CheckDragStopSound(enabledModsList);
CheckDragStopSound(disabledModsList);
}
else if (enabledModsList.DraggedElement != null)
{
SetDragOrigin(enabledModsList);
}
else if (disabledModsList.DraggedElement != null)
{
SetDragOrigin(disabledModsList);
}
void SetDragOrigin(GUIListBox? listBox)
{
draggedElement = listBox?.DraggedElement;
draggedElementOrigin = listBox;
}
void CheckDragStopSound(GUIListBox listBox)
{
listBox.PlaySoundOnDragStop = listBox.DraggedElement != null && draggedElementOrigin != listBox;
}
}
}
protected override void UpdateModListItemVisibility()
{
string str = modsListFilter.Text;
enabledRegularModsList.Content.Children.Concat(disabledRegularModsList.Content.Children)
.ForEach(c => c.Visible = !(c.UserData is ContentPackage p)
|| ModNameMatches(p, str) && ModMatchesTickboxes(p, c));
}
private bool ModMatchesTickboxes(ContentPackage p, GUIComponent guiItem)
{
var iconBtn = guiItem.GetChild<GUILayoutGroup>()?.GetAllChildren<GUIButton>().Last();
bool matches = false;
matches |= modsListFilterTickboxes[Filter.ShowLocal].Selected
&& ContentPackageManager.LocalPackages.Contains(p);
matches |= modsListFilterTickboxes[Filter.ShowPublished].Selected
&& (ContentPackageManager.WorkshopPackages.Contains(p)
&& iconBtn?.Style?.Identifier == "WorkshopMenu.PublishedIcon");
matches |= modsListFilterTickboxes[Filter.ShowWorkshop].Selected
&& (ContentPackageManager.WorkshopPackages.Contains(p)
&& iconBtn?.Style?.Identifier != "WorkshopMenu.PublishedIcon");
if (modsListFilterTickboxes[Filter.ShowOnlySubs].Selected
&& modsListFilterTickboxes[Filter.ShowOnlyItemAssemblies].Selected
&& p.Files.All(f => f is BaseSubFile || f is ItemAssemblyFile))
{
//Both the subs-only tickbox and the item-assembly-only tickbox
//are enabled, and all files match either of them so show this mod
}
else if (modsListFilterTickboxes[Filter.ShowOnlySubs].Selected
&& p.Files.Any(f => !(f is BaseSubFile)))
{
matches = false;
}
else if (modsListFilterTickboxes[Filter.ShowOnlyItemAssemblies].Selected
&& p.Files.Any(f => !(f is ItemAssemblyFile)))
{
matches = false;
}
return matches;
}
private void PrepareToShowModInfo(ContentPackage mod)
{
TaskPool.Add($"PrepareToShow{mod.SteamWorkshopId}Info", SteamManager.Workshop.GetItem(mod.SteamWorkshopId),
t =>
{
if (!t.TryGetResult(out Steamworks.Ugc.Item? item)) { return; }
if (item is null) { return; }
onInstalledInfoButtonHit(item.Value);
});
}
public void PopulateInstalledModLists(bool forceRefreshEnabled = false, bool refreshDisabled = true)
{
bulkUpdateButton.Enabled = false;
bulkUpdateButton.ToolTip = "";
ContentPackageManager.UpdateContentPackageList();
SwapDropdownValues<CorePackage>(enabledCoreDropdown,
(p) => p.Name,
ContentPackageManager.CorePackages.ToArray(),
ContentPackageManager.EnabledPackages.Core!,
(p) => { });
void addRegularModToList(RegularPackage mod, GUIListBox list)
{
var modFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), list.Content.RectTransform),
style: "ListBoxElement")
{
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)));
}
var labelConditions
= (parentList == enabledRegularModsList, parentList.AllSelected.Count > 1);
Identifier swapLabel = (labelConditions switch
{
(true, true) => "EnableSelectedWorkshopMods",
(true, false) => "EnableWorkshopMod",
(false, true) => "DisableSelectedWorkshopMods",
(false, false) => "DisableWorkshopMod"
}).ToIdentifier();
contextMenuOptions.Add(new ContextMenuOption(swapLabel,
isEnabled: true, onSelected: currentSwapFunc ?? noop));
var selectedMods = parentList.AllSelected.Select(it => it.UserData)
.OfType<ContentPackage>().ToArray();
if (selectedMods.All(ContentPackageManager.LocalPackages.Contains) && selectedMods.Length > 1)
{
contextMenuOptions.Add(new ContextMenuOption("MergeSelectedMods".ToIdentifier(), isEnabled: true,
onSelected: () => ModMerger.AskMerge(selectedMods)));
}
GUIButton? iconBtn(GUIComponent component) => component.GetChild<GUILayoutGroup>()?.GetAllChildren<GUIButton>().Last();
if (selectedMods.All(ContentPackageManager.WorkshopPackages.Contains)
&& parentList.AllSelected.All(c => iconBtn(c)?.Style?.Identifier == "WorkshopMenu.DownloadedIcon")
&& selectedMods.Length > 0)
{
contextMenuOptions.Add(new ContextMenuOption(
(selectedMods.Length > 1 ? "UnsubscribeFromAllSelected" : "WorkshopItemUnsubscribe").ToIdentifier(),
isEnabled: true,
onSelected: () =>
{
TaskPool.Add($"UnsubFromSelected", Task.WhenAll(selectedMods.Select(m => SteamManager.Workshop.GetItem(m.SteamWorkshopId))),
t =>
{
if (!t.TryGetResult(out Steamworks.Ugc.Item?[] items)) { return; }
items.ForEach(it =>
{
if (!(it is { } item)) { return; }
item.Unsubscribe();
SteamManager.Workshop.Uninstall(item);
PopulateInstalledModLists();
});
});
}));
}
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)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dragIndicator = new GUIButton(new RectTransform((0.5f, 0.5f), frameContent.RectTransform, scaleBasis: ScaleBasis.BothHeight),
style: "GUIDragIndicator")
{
CanBeFocused = false
};
var modNameScissor = new GUIScissorComponent(new RectTransform((0.8f, 1.0f), frameContent.RectTransform))
{
CanBeFocused = false
};
var modName = new GUITextBlock(new RectTransform(Vector2.One, modNameScissor.Content.RectTransform),
text: mod.Name)
{
CanBeFocused = false
};
if (mod.Errors.Any())
{
CreateModErrorInfo(mod, modFrame, modName);
}
if (ContentPackageManager.LocalPackages.Contains(mod))
{
var editButton = new GUIButton(new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: "WorkshopMenu.EditButton")
{
OnClicked = (button, o) =>
{
ToolBox.OpenFileWithShell(mod.Dir);
return false;
},
ToolTip = TextManager.Get("OpenLocalModInExplorer")
};
}
else if (ContentPackageManager.WorkshopPackages.Contains(mod))
{
var infoButton = new GUIButton(
new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: null)
{
CanBeSelected = false,
OnClicked = (button, o) =>
{
PrepareToShowModInfo(mod);
return false;
}
};
if (!SteamManager.IsInitialized)
{
infoButton.Enabled = false;
}
TaskPool.Add(
$"DetermineUpdateRequired{mod.SteamWorkshopId}",
mod.IsUpToDate(),
t =>
{
if (!t.TryGetResult(out bool isUpToDate)) { return; }
if (!isUpToDate)
{
infoButton.CanBeSelected = true;
infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]);
infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable");
bulkUpdateButton.Enabled = true;
bulkUpdateButton.ToolTip = TextManager.Get("ModUpdatesAvailable");
}
});
}
}
void addRegularModsToList(IEnumerable<RegularPackage> mods, GUIListBox list)
{
list.ClearChildren();
foreach (var mod in mods)
{
addRegularModToList(mod, list);
}
}
var enabledMods =
(forceRefreshEnabled || (enabledRegularModsList.Content.CountChildren + disabledRegularModsList.Content.CountChildren == 0)
? ContentPackageManager.EnabledPackages.Regular
: enabledRegularModsList.Content.Children
.Select(c => c.UserData)
.OfType<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();
}
}
}
@@ -151,7 +151,10 @@ namespace Barotrauma.Steam
onDeselected: () => itemList?.Deselect(),
out var select, out var deselect);
itemList = new GUIListBox(new RectTransform(Vector2.One, outerContainer.Content.RectTransform));
itemList = new GUIListBox(new RectTransform(Vector2.One, outerContainer.Content.RectTransform))
{
PlaySoundOnSelect = true,
};
itemList.RectTransform.SetAsFirstChild();
workshopItemList = itemList;
@@ -0,0 +1,256 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.IO;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
readonly struct ModListPreset
{
public const string SavePath = "ModLists";
public enum ModType
{
Vanilla,
Local,
Workshop
}
public readonly string Name;
public readonly CorePackage CorePackage;
public readonly ImmutableArray<RegularPackage> RegularPackages;
public ModListPreset(XDocument doc)
{
Name = doc.Root!.GetAttributeString("name", "");
CorePackage corePackage = ContentPackageManager.VanillaCorePackage!;
List<RegularPackage> regularPackages = new List<RegularPackage>();
void addPkg(ContentPackage pkg)
{
if (pkg is CorePackage core) { corePackage = core; }
else if (pkg is RegularPackage reg) { regularPackages.Add(reg); }
}
foreach (var element in doc.Root!.Elements())
{
ModType modType = Enum.TryParse<ModType>(element.Name.LocalName, ignoreCase: true, out var mt) ? mt : ModType.Local;
switch (modType)
{
case ModType.Vanilla:
CorePackage = ContentPackageManager.VanillaCorePackage!;
break;
case ModType.Workshop:
{
var id = element.GetAttributeUInt64("id", 0);
var pkg = ContentPackageManager.WorkshopPackages.FirstOrDefault(p => p.SteamWorkshopId == id);
if (id != 0 && pkg != null)
{
addPkg(pkg);
}
}
break;
case ModType.Local:
{
var name = element.GetAttributeString("name", "");
var pkg = ContentPackageManager.LocalPackages.FirstOrDefault(p => p.NameMatches(name));
if (!name.IsNullOrEmpty() && pkg != null)
{
addPkg(pkg);
}
}
break;
}
}
CorePackage = corePackage;
RegularPackages = regularPackages.ToImmutableArray();
}
public ModListPreset(string name, CorePackage corePackage, IReadOnlyList<RegularPackage> regularPackages)
{
Name = name;
CorePackage = corePackage;
RegularPackages = regularPackages.ToImmutableArray();
}
public RichString GetTooltip()
{
LocalizedString retVal = $"‖color:gui.orange‖{Name}‖end‖" //TODO: we need a RichString builder
+ "\n " + TextManager.AddPunctuation(':', TextManager.Get("CorePackage"))
+ "\n - " + CorePackage.Name;
if (RegularPackages.Any())
{
retVal += "\n " + TextManager.AddPunctuation(':', TextManager.Get("RegularPackages"))
+ "\n - "
+ LocalizedString.Join("\n - ", RegularPackages.Select(p => (LocalizedString)p.Name));
}
return RichString.Rich(retVal);
}
public void Save()
{
XDocument newDoc = new XDocument();
XElement newRoot = new XElement("mods", new XAttribute("name", Name));
newDoc.Add(newRoot);
ModType determineType(ContentPackage pkg)
{
if (pkg == ContentPackageManager.VanillaCorePackage) { return ModType.Vanilla; }
if (ContentPackageManager.WorkshopPackages.Contains(pkg)) { return ModType.Workshop; }
return ModType.Local;
}
void writePkgElem(ContentPackage pkg)
{
var pkgType = determineType(pkg);
var pkgElem = new XElement(pkgType.ToString());
switch (pkgType)
{
case ModType.Workshop:
pkgElem.SetAttributeValue("name", pkg.Name);
pkgElem.SetAttributeValue("id", pkg.SteamWorkshopId.ToString());
break;
case ModType.Local:
pkgElem.SetAttributeValue("name", pkg.Name);
break;
}
newRoot.Add(pkgElem);
}
writePkgElem(CorePackage);
RegularPackages.ForEach(writePkgElem);
if (!Directory.Exists(SavePath)) { Directory.CreateDirectory(SavePath); }
newDoc.SaveSafe(Path.Combine(SavePath, ToolBox.RemoveInvalidFileNameChars($"{Name}.xml")));
}
}
}
namespace Barotrauma.Steam
{
sealed partial class MutableWorkshopMenu : WorkshopMenu
{
private bool OpenLoadPreset(GUIButton _, object __)
{
OpenLoadPreset();
return false;
}
private void OpenLoadPreset()
{
var msgBox = new GUIMessageBox(
TextManager.Get("LoadModListPresetHeader"),
"",
buttons: new [] { TextManager.Get("Load"), TextManager.Get("Cancel") },
relativeSize: (0.4f, 0.6f));
var presetListBox = new GUIListBox(new RectTransform((1.0f, 0.7f), msgBox.Content.RectTransform));
(string Path, XDocument? Doc) tryLoadXml(string path)
=> (path, XMLExtensions.TryLoadXml(path));
var presets = Directory.Exists(ModListPreset.SavePath)
? Directory.GetFiles(ModListPreset.SavePath)
.Select(tryLoadXml)
.Where(d => d.Doc != null)
.ToArray()
: Array.Empty<(string Path, XDocument? Doc)>();
foreach (var doc in presets)
{
ModListPreset preset = new ModListPreset(doc.Doc!);
var presetFrame = new GUIFrame(new RectTransform((1.0f, 0.09f), presetListBox.Content.RectTransform),
style: "ListBoxElement")
{
UserData = preset,
ToolTip = preset.GetTooltip()
};
new GUITextBlock(new RectTransform(Vector2.One, presetFrame.RectTransform), preset.Name)
{
CanBeFocused = false
};
var deleteBtn
= new GUIButton(new RectTransform((0.2f, 1.0f), presetFrame.RectTransform, Anchor.CenterRight),
TextManager.Get("Delete"), style: "GUIButtonSmall")
{
OnClicked = (button, o) =>
{
File.Delete(doc.Path);
presetListBox.Content.RemoveChild(presetFrame);
return false;
}
};
}
msgBox.Buttons[0].OnClicked = (button, o) =>
{
if (presetListBox.SelectedData is ModListPreset preset)
{
var allChildren = enabledRegularModsList.Content.Children
.Concat(disabledRegularModsList.Content.Children)
.ToArray();
enabledRegularModsList.ClearChildren();
disabledRegularModsList.ClearChildren();
var toEnable =
allChildren.Where(c => c.UserData is RegularPackage p
&& preset.RegularPackages.Contains(p))
.OrderBy(c => c.UserData is RegularPackage p ? preset.RegularPackages.IndexOf(p) : int.MaxValue)
.ToArray();
var toDisable = allChildren.Where(c => !toEnable.Contains(c)).ToArray();
toEnable.ForEach(c => c.RectTransform.Parent = enabledRegularModsList.Content.RectTransform);
toDisable.ForEach(c => c.RectTransform.Parent = disabledRegularModsList.Content.RectTransform);
enabledCoreDropdown.SelectItem(preset.CorePackage);
}
msgBox.Close();
return false;
};
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
private bool OpenSavePreset(GUIButton _, object __)
{
OpenSavePreset();
return false;
}
private void OpenSavePreset()
{
var msgBox = new GUIMessageBox(
TextManager.Get("SaveModListPresetHeader"),
"",
buttons: new [] { TextManager.Get("Save"), TextManager.Get("Cancel") },
relativeSize: (0.4f, 0.2f));
var nameBox = new GUITextBox(new RectTransform((1.0f, 0.3f), msgBox.Content.RectTransform), "");
msgBox.Buttons[0].OnClicked = (button, o) =>
{
if (nameBox.Text.IsNullOrEmpty())
{
nameBox.Flash(GUIStyle.Red);
return false;
}
if (enabledCoreDropdown.SelectedData is CorePackage corePackage)
{
ModListPreset preset = new ModListPreset(nameBox.Text,
corePackage,
enabledRegularModsList.Content.Children
.Select(c => c.UserData)
.OfType<RegularPackage>().ToArray());
preset.Save();
}
msgBox.Close();
return false;
};
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
}
}
@@ -3,9 +3,9 @@ using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
namespace Barotrauma.Steam
@@ -20,20 +20,20 @@ namespace Barotrauma.Steam
Publish
}
private enum Filter
{
ShowLocal,
ShowWorkshop,
ShowPublished,
ShowOnlySubs,
ShowOnlyItemAssemblies
}
private readonly GUILayoutGroup tabber;
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
private readonly GUIFrame contentFrame;
private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
private readonly GUIDropDown enabledCoreDropdown;
private readonly GUIListBox enabledRegularModsList;
private readonly GUIListBox disabledRegularModsList;
private readonly Action<ItemOrPackage> onInstalledInfoButtonHit;
private readonly GUITextBox modsListFilter;
private readonly GUIButton bulkUpdateButton;
private CancellationTokenSource taskCancelSrc = new CancellationTokenSource();
private readonly HashSet<SteamManager.Workshop.ItemThumbnail> itemThumbnails = new HashSet<SteamManager.Workshop.ItemThumbnail>();
@@ -41,7 +41,7 @@ namespace Barotrauma.Steam
private readonly GUIListBox selfModsList;
private uint memSubscribedModCount = 0;
public MutableWorkshopMenu(GUIFrame parent) : base(parent)
{
var mainLayout
@@ -62,6 +62,7 @@ namespace Barotrauma.Steam
out disabledRegularModsList,
out onInstalledInfoButtonHit,
out modsListFilter,
out modsListFilterTickboxes,
out bulkUpdateButton);
CreatePopularModsTab(out popularModsList);
CreatePublishTab(out selfModsList);
@@ -69,45 +70,6 @@ namespace Barotrauma.Steam
SelectTab(Tab.InstalledMods);
}
private void UpdateSubscribedModInstalls()
{
if (!SteamManager.IsInitialized) { return; }
uint numSubscribedMods = SteamManager.GetNumSubscribedItems();
if (numSubscribedMods == memSubscribedModCount) { return; }
memSubscribedModCount = numSubscribedMods;
var subscribedIds = SteamManager.GetSubscribedItems().ToHashSet();
var installedIds = ContentPackageManager.WorkshopPackages.Select(p => p.SteamWorkshopId).ToHashSet();
foreach (var id in subscribedIds.Where(id2 => !installedIds.Contains(id2)))
{
Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id);
if (!item.IsDownloading && !SteamManager.Workshop.IsInstalling(item))
{
SteamManager.Workshop.DownloadModThenEnqueueInstall(item);
}
}
TaskPool.Add("RemoveUnsubscribedItems", SteamManager.Workshop.GetPublishedItems(), t =>
{
if (!t.TryGetResult(out ISet<Steamworks.Ugc.Item> publishedItems)) { return; }
var allRequiredInstalled = subscribedIds.Union(publishedItems.Select(it => it.Id)).ToHashSet();
bool needsRefresh = false;
foreach (var id in installedIds.Where(id2 => !allRequiredInstalled.Contains(id2)))
{
Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id);
SteamManager.Workshop.Uninstall(item);
needsRefresh = true;
}
if (needsRefresh)
{
PopulateInstalledModLists();
}
});
}
private void SwitchContent(GUIFrame newContent)
{
contentFrame.Children.ForEach(c => c.Visible = false);
@@ -161,460 +123,6 @@ namespace Barotrauma.Steam
return content;
}
private static (GUILayoutGroup Left, GUIFrame center, GUILayoutGroup Right) CreateSidebars(
GUIComponent parent,
float leftWidth = 0.3875f,
float centerWidth = 0.025f,
float rightWidth = 0.5875f,
bool split = false,
float height = 1.0f)
{
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform((1.0f, height), parent.RectTransform), isHorizontal: true);
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((leftWidth, 1.0f), layout.RectTransform), isHorizontal: false);
var center = new GUIFrame(new RectTransform((centerWidth, 1.0f), layout.RectTransform), style: null);
if (split)
{
new GUICustomComponent(new RectTransform(Vector2.One, center.RectTransform),
onDraw: (sb, c) =>
{
sb.DrawLine((c.Rect.Center.X, c.Rect.Top), (c.Rect.Center.X, c.Rect.Bottom), GUIStyle.TextColorDim, 2f);
});
}
GUILayoutGroup right = new GUILayoutGroup(new RectTransform((rightWidth, 1.0f), layout.RectTransform), isHorizontal: false);
return (left, center, right);
}
private void HandleDraggingAcrossModLists(GUIListBox from, GUIListBox to)
{
if (to.Rect.Contains(PlayerInput.MousePosition) && from.DraggedElement != null)
{
//move the dragged elements to the index determined previously
var draggedElement = from.DraggedElement;
var selected = from.AllSelected.ToList();
selected.Sort((a, b) => from.Content.GetChildIndex(a) - from.Content.GetChildIndex(b));
float oldCount = to.Content.CountChildren;
float newCount = oldCount + selected.Count;
var offset = draggedElement.RectTransform.AbsoluteOffset;
offset += from.Content.Rect.Location;
offset -= to.Content.Rect.Location;
for (int i = 0; i < selected.Count; i++)
{
var c = selected[i];
c.Parent.RemoveChild(c);
c.RectTransform.Parent = to.Content.RectTransform;
c.RectTransform.RepositionChildInHierarchy((int)oldCount+i);
}
from.DraggedElement = null;
from.Deselect();
from.RecalculateChildren();
from.RectTransform.RecalculateScale(true);
to.RecalculateChildren();
to.RectTransform.RecalculateScale(true);
to.Select(selected);
//recalculate the dragged element's offset so it doesn't jump around
draggedElement.RectTransform.AbsoluteOffset = offset;
to.DraggedElement = draggedElement;
to.BarScroll *= (oldCount / newCount);
}
}
private Action? currentSwapFunc = null;
private void SetSwapFunc(GUIListBox from, GUIListBox to)
{
currentSwapFunc = () =>
{
to.Deselect();
var selected = from.AllSelected.ToArray();
foreach (var frame in selected)
{
frame.Parent.RemoveChild(frame);
frame.RectTransform.Parent = to.Content.RectTransform;
}
from.RecalculateChildren();
from.RectTransform.RecalculateScale(true);
to.RecalculateChildren();
to.RectTransform.RecalculateScale(true);
to.Select(selected);
};
}
private void CreateInstalledModsTab(
out GUIDropDown enabledCoreDropdown,
out GUIListBox enabledRegularModsList,
out GUIListBox disabledRegularModsList,
out Action<ItemOrPackage> onInstalledInfoButtonHit,
out GUITextBox modsListFilter,
out GUIButton bulkUpdateButton)
{
GUIFrame content = CreateNewContentFrame(Tab.InstalledMods);
CreateWorkshopItemDetailContainer(
content,
out var outerContainer,
onSelected: (itemOrPackage, selectedFrame) =>
{
if (itemOrPackage.TryGet(out Steamworks.Ugc.Item item)) { PopulateFrameWithItemInfo(item, selectedFrame); }
},
onDeselected: () => PopulateInstalledModLists(),
out onInstalledInfoButtonHit, out var deselect);
GUILayoutGroup mainLayout =
new GUILayoutGroup(new RectTransform(Vector2.One, outerContainer.Content.RectTransform), childAnchor: Anchor.TopCenter);
mainLayout.RectTransform.SetAsFirstChild();
var (topLeft, _, topRight) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.13f);
topLeft.Stretch = true;
Label(topLeft, TextManager.Get("enabledcore"), GUIStyle.SubHeadingFont, heightScale: 1.0f);
enabledCoreDropdown = Dropdown<CorePackage>(topLeft,
(p) => p.Name,
ContentPackageManager.CorePackages.ToArray(),
ContentPackageManager.EnabledPackages.Core!,
(p) => { },
heightScale: 1.0f / 13.0f);
Label(topLeft, "", GUIStyle.SubHeadingFont, heightScale: 1.0f);
topRight.ChildAnchor = Anchor.CenterLeft;
var topRightButtons = new GUILayoutGroup(new RectTransform((1.0f, 0.5f), topRight.RectTransform),
isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
RelativeSpacing = 0.05f
};
void padTopRight(float width=1.0f)
{
new GUIFrame(new RectTransform((width, 1.0f), topRightButtons.RectTransform), style: null);
}
padTopRight();
//TODO: put stuff here
padTopRight(width: 3.0f);
var refreshListsButton
= new GUIButton(
new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight),
text: "", style: "GUIReloadButton")
{
OnClicked = (b, o) =>
{
PopulateInstalledModLists();
return false;
},
ToolTip = TextManager.Get("RefreshModLists")
};
bulkUpdateButton
= new GUIButton(
new RectTransform(Vector2.One, topRightButtons.RectTransform, scaleBasis: ScaleBasis.BothHeight),
text: "", style: "GUIUpdateButton")
{
OnClicked = (b, o) =>
{
BulkDownloader.PrepareUpdates();
return false;
},
Enabled = false
};
padTopRight(width: 0.1f);
var (left, center, right) = CreateSidebars(mainLayout, centerWidth: 0.05f, leftWidth: 0.475f, rightWidth: 0.475f, height: 0.8f);
right.ChildAnchor = Anchor.TopRight;
//enabled mods
Label(left, TextManager.Get("enabledregular"), GUIStyle.SubHeadingFont);
var enabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), left.RectTransform))
{
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple,
HideDraggedElement = true
};
enabledRegularModsList = enabledModsList;
//disabled mods
Label(right, TextManager.Get("disabledregular"), GUIStyle.SubHeadingFont);
var disabledModsList = new GUIListBox(new RectTransform((1.0f, 0.93f), right.RectTransform))
{
CurrentDragMode = GUIListBox.DragMode.DragOutsideBox,
CurrentSelectMode = GUIListBox.SelectMode.RequireShiftToSelectMultiple,
HideDraggedElement = true
};
disabledRegularModsList = disabledModsList;
var centerButton =
new GUIButton(
new RectTransform(Vector2.One * 0.95f, center.RectTransform, scaleBasis: ScaleBasis.BothWidth,
anchor: Anchor.Center),
style: "GUIButtonToggleLeft")
{
Visible = false,
OnClicked = (button, o) =>
{
currentSwapFunc?.Invoke();
return false;
}
};
enabledModsList.OnSelected = (frame, o) =>
{
disabledModsList.Deselect();
centerButton.Visible = true;
centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleRight"));
SetSwapFunc(enabledModsList, disabledModsList);
return true;
};
disabledModsList.OnSelected = (frame, o) =>
{
enabledModsList.Deselect();
centerButton.Visible = true;
centerButton.ApplyStyle(GUIStyle.GetComponentStyle("GUIButtonToggleLeft"));
SetSwapFunc(disabledModsList, enabledModsList);
return true;
};
var searchBox = CreateSearchBox(mainLayout, width: 0.5f);
modsListFilter = searchBox;
new GUICustomComponent(new RectTransform(Vector2.Zero, content.RectTransform),
onUpdate: (f, component) =>
{
HandleDraggingAcrossModLists(enabledModsList, disabledModsList);
HandleDraggingAcrossModLists(disabledModsList, enabledModsList);
if (PlayerInput.PrimaryMouseButtonClicked()
&& !GUI.IsMouseOn(enabledModsList)
&& !GUI.IsMouseOn(disabledModsList)
&& GUIContextMenu.CurrentContextMenu is null)
{
enabledModsList.Deselect();
disabledModsList.Deselect();
}
else if (!PlayerInput.IsCtrlDown() && !PlayerInput.IsShiftDown() && PlayerInput.DoubleClicked())
{
currentSwapFunc?.Invoke();
}
},
onDraw: (spriteBatch, component) =>
{
enabledModsList.DraggedElement?.DrawManually(spriteBatch, true, true);
disabledModsList.DraggedElement?.DrawManually(spriteBatch, true, true);
});
}
protected override void UpdateModListItemVisibility()
{
string str = modsListFilter.Text;
enabledRegularModsList.Content.Children.Concat(disabledRegularModsList.Content.Children)
.ForEach(c => c.Visible = str.IsNullOrWhiteSpace()
|| (c.UserData is ContentPackage p
&& p.Name.Contains(str, StringComparison.OrdinalIgnoreCase)));
}
private void PrepareToShowModInfo(ContentPackage mod)
{
TaskPool.Add($"PrepareToShow{mod.SteamWorkshopId}Info", SteamManager.Workshop.GetItem(mod.SteamWorkshopId),
t =>
{
if (!t.TryGetResult(out Steamworks.Ugc.Item? item)) { return; }
if (item is null) { return; }
onInstalledInfoButtonHit(item.Value);
});
}
public void PopulateInstalledModLists(bool forceRefreshEnabled = false, bool refreshDisabled = true)
{
bulkUpdateButton.Enabled = false;
bulkUpdateButton.ToolTip = "";
ContentPackageManager.UpdateContentPackageList();
SwapDropdownValues<CorePackage>(enabledCoreDropdown,
(p) => p.Name,
ContentPackageManager.CorePackages.ToArray(),
ContentPackageManager.EnabledPackages.Core!,
(p) => { });
void addRegularModToList(RegularPackage mod, GUIListBox list)
{
var modFrame = new GUIFrame(new RectTransform((1.0f, 0.08f), list.Content.RectTransform),
style: "ListBoxElement")
{
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)
{
Stretch = true,
RelativeSpacing = 0.02f
};
var dragIndicator = new GUIButton(new RectTransform((0.5f, 0.5f), frameContent.RectTransform, scaleBasis: ScaleBasis.BothHeight),
style: "GUIDragIndicator")
{
CanBeFocused = false
};
var modNameScissor = new GUIScissorComponent(new RectTransform((0.8f, 1.0f), frameContent.RectTransform))
{
CanBeFocused = false
};
var modName = new GUITextBlock(new RectTransform(Vector2.One, modNameScissor.Content.RectTransform),
text: mod.Name)
{
CanBeFocused = false
};
if (mod.Errors.Any())
{
CreateModErrorInfo(mod, modFrame, modName);
}
if (ContentPackageManager.LocalPackages.Contains(mod))
{
var editButton = new GUIButton(new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: "WorkshopMenu.EditButton")
{
OnClicked = (button, o) =>
{
ToolBox.OpenFileWithShell(mod.Dir);
return false;
},
ToolTip = TextManager.Get("OpenLocalModInExplorer")
};
}
else if (ContentPackageManager.WorkshopPackages.Contains(mod))
{
var infoButton = new GUIButton(
new RectTransform(Vector2.One, frameContent.RectTransform, scaleBasis: ScaleBasis.Smallest), "",
style: null)
{
CanBeSelected = false,
OnClicked = (button, o) =>
{
PrepareToShowModInfo(mod);
return false;
}
};
if (!SteamManager.IsInitialized)
{
infoButton.Enabled = false;
}
TaskPool.Add(
$"DetermineUpdateRequired{mod.SteamWorkshopId}",
mod.IsUpToDate(),
t =>
{
if (!t.TryGetResult(out bool isUpToDate)) { return; }
if (!isUpToDate)
{
infoButton.CanBeSelected = true;
infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]);
infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable");
bulkUpdateButton.Enabled = true;
bulkUpdateButton.ToolTip = TextManager.Get("ModUpdatesAvailable");
}
});
}
}
void addRegularModsToList(IEnumerable<RegularPackage> mods, GUIListBox list)
{
list.ClearChildren();
foreach (var mod in mods)
{
addRegularModToList(mod, list);
}
}
var enabledMods =
(forceRefreshEnabled || (enabledRegularModsList.Content.CountChildren + disabledRegularModsList.Content.CountChildren == 0)
? ContentPackageManager.EnabledPackages.Regular
: enabledRegularModsList.Content.Children
.Select(c => c.UserData)
.OfType<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();
}
private void CreatePopularModsTab(out GUIListBox popularModsList)
{
GUIFrame content = CreateNewContentFrame(Tab.PopularMods);
@@ -106,10 +106,8 @@ namespace Barotrauma.Steam
=> new GUIFrame(new RectTransform(Vector2.Zero, parent.RectTransform), style: null)
{ UserData = new ActionCarrier(id, action) };
protected GUITextBox CreateSearchBox(GUILayoutGroup mainLayout, float width = 1.0f, float heightScale = 1.0f)
protected GUITextBox CreateSearchBox(RectTransform searchRectT)
{
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},
@@ -142,7 +140,8 @@ namespace Barotrauma.Steam
const int maxErrorsToShow = 5;
nameText.TextColor = GUIStyle.Red;
uiElement.ToolTip =
TextManager.GetWithVariable("contentpackagehaserrors", "[packagename]", mod.Name) + '\n' + string.Join('\n', mod.Errors.Take(maxErrorsToShow).Select(e => e.error));
TextManager.GetWithVariable("contentpackagehaserrors", "[packagename]", mod.Name)
+ '\n' + string.Join('\n', mod.Errors.Take(maxErrorsToShow).Select(e => e.Message));
if (mod.Errors.Count() > maxErrorsToShow)
{
uiElement.ToolTip += '\n' + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (mod.Errors.Count() - maxErrorsToShow).ToString());
@@ -1,3 +1,5 @@
using System;
#nullable enable
namespace Barotrauma.Steam
@@ -7,5 +9,8 @@ namespace Barotrauma.Steam
public WorkshopMenu(GUIFrame parent) { }
protected abstract void UpdateModListItemVisibility();
protected bool ModNameMatches(ContentPackage p, string query)
=> p.Name.Contains(query, StringComparison.OrdinalIgnoreCase);
}
}