using Barotrauma.Tutorials; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using Barotrauma.IO; using System.Linq; using System.Xml.Linq; using System.Globalization; using Barotrauma.Extensions; namespace Barotrauma { class SinglePlayerCampaignSetupUI : CampaignSetupUI { public CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus { get; private set; } private GUIButton nextButton; private GUILayoutGroup characterInfoColumns; public SinglePlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, IEnumerable submarines, IEnumerable saveFiles = null) : base(newGameContainer, loadGameContainer) { UpdateNewGameMenu(submarines); UpdateLoadMenu(saveFiles); } private int currentPage = 0; private GUIListBox pageContainer; public void Update() { float targetScroll = (float)currentPage / ((float)pageContainer.Content.CountChildren - 1); pageContainer.BarScroll = MathHelper.Lerp(pageContainer.BarScroll, targetScroll, 0.2f); if (MathUtils.NearlyEqual(pageContainer.BarScroll, targetScroll, 0.001f)) { pageContainer.BarScroll = targetScroll; } for (int i=0; i { if (c is GUIDropDown dd) { dd.Dropped = false; } c.CanBeFocused = (i == currentPage); }); } var previewListBox = subPreviewContainer.GetAllChildren().FirstOrDefault(); previewListBox?.GetAllChildren()?.ForEach(c => { c.CanBeFocused = false; }); } private void UpdateNewGameMenu(IEnumerable submarines) { pageContainer = new GUIListBox(new RectTransform(Vector2.One, newGameContainer.RectTransform), style: null, isHorizontal: true) { ScrollBarEnabled = false, ScrollBarVisible = false, HoverCursor = CursorState.Default }; GUILayoutGroup createPageLayout() { var containerItem = new GUIFrame(new RectTransform(Vector2.One, pageContainer.Content.RectTransform), style: null); return new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, containerItem.RectTransform, Anchor.Center)); } CreateFirstPage(createPageLayout(), submarines); CreateSecondPage(createPageLayout()); pageContainer.RecalculateChildren(); pageContainer.GetAllChildren().ForEach(c => { c.ClampMouseRectToParent = true; }); pageContainer.GetAllChildren().ForEach(dd => { dd.ListBox.ClampMouseRectToParent = false; dd.ListBox.Content.ClampMouseRectToParent = false; }); SetPage(0); } private void CreateFirstPage(GUILayoutGroup firstPageLayout, IEnumerable submarines) { firstPageLayout.RelativeSpacing = 0.02f; var columnContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), firstPageLayout.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.02f }; var leftColumn = new GUILayoutGroup(new RectTransform(Vector2.One, columnContainer.RectTransform)) { Stretch = true, RelativeSpacing = 0.015f }; var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.5f, 1.0f), columnContainer.RectTransform)) { Stretch = true, RelativeSpacing = 0.015f }; columnContainer.Recalculate(); // New game left side new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("SaveName"), font: GUI.SubHeadingFont); saveNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, string.Empty) { textFilterFunction = (string str) => { return ToolBox.RemoveInvalidFileNameChars(str); } }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.02f), leftColumn.RectTransform) { MinSize = new Point(0, 20) }, TextManager.Get("MapSeed"), font: GUI.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: GUI.SubHeadingFont); var moddedDropdown = new GUIDropDown(new RectTransform(new Vector2(1f, 0.02f), leftColumn.RectTransform), "", 3); moddedDropdown.AddItem(TextManager.Get("clientpermission.all"), CategoryFilter.All); moddedDropdown.AddItem(TextManager.Get("servertag.modded.false"), CategoryFilter.Vanilla); moddedDropdown.AddItem(TextManager.Get("customrank"), CategoryFilter.Custom); moddedDropdown.Select(0); var filterContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), isHorizontal: true) { Stretch = true }; subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform)) { ScrollBarVisible = true }; var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font); var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font, createClearButton: true); filterContainer.RectTransform.MinSize = searchBox.RectTransform.MinSize; searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; searchBox.OnTextChanged += (textBox, text) => { FilterSubs(subList, text); return true; }; moddedDropdown.OnSelected = (component, data) => { searchBox.Text = string.Empty; subFilter = (CategoryFilter)data; UpdateSubList(SubmarineInfo.SavedSubmarines); return true; }; subList.OnSelected = OnSubSelected; // New game right side subPreviewContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform)) { Stretch = true }; var firstPageButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), firstPageLayout.RectTransform), childAnchor: Anchor.BottomLeft, isHorizontal: true) { RelativeSpacing = 0.025f }; InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1f), firstPageButtonContainer.RectTransform), "", font: GUI.Style.Font, textColor: GUI.Style.Green, textAlignment: Alignment.CenterLeft) { TextGetter = () => { int initialMoney = CampaignMode.InitialMoney; if (subList.SelectedData is SubmarineInfo subInfo) { initialMoney -= subInfo.Price; } initialMoney = Math.Max(initialMoney, 0); 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(); return true; } }; nextButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), firstPageButtonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("Next")) { OnClicked = (GUIButton btn, object userData) => { SetPage(1); 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, OnClicked = (btn, userdata) => { GameMain.Instance.ShowCampaignDisclaimer(); return true; } }; disclaimerBtn.RectTransform.MaxSize = new Point((int)(30 * GUI.Scale)); columnContainer.Recalculate(); leftColumn.Recalculate(); rightColumn.Recalculate(); if (submarines != null) { UpdateSubList(submarines); } } private void CreateSecondPage(GUILayoutGroup secondPageLayout) { secondPageLayout.RelativeSpacing = 0.01f; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.04f), secondPageLayout.RectTransform), TextManager.Get("Crew"), font: GUI.Style.SubHeadingFont, textAlignment: Alignment.TopLeft); characterInfoColumns = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.86f), secondPageLayout.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.01f }; var secondPageButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), secondPageLayout.RectTransform), childAnchor: Anchor.BottomLeft, isHorizontal: true) { RelativeSpacing = 0.2f }; var backButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), secondPageButtonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("Back")) { OnClicked = (GUIButton btn, object userData) => { SetPage(0); return false; } }; StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1f), secondPageButtonContainer.RectTransform, Anchor.BottomRight), TextManager.Get("StartCampaignButton")) { OnClicked = FinishSetup }; } public void RandomizeCrew() { var characterInfos = new List<(CharacterInfo Info, JobPrefab Job)>(); foreach (JobPrefab jobPrefab in JobPrefab.Prefabs) { for (int i = 0; i < jobPrefab.InitialCount; i++) { var variant = Rand.Range(0, jobPrefab.Variants); characterInfos.Add((new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant), jobPrefab)); } } characterInfoColumns.ClearChildren(); CharacterMenus?.ForEach(m => m.Dispose()); CharacterMenus = new CharacterInfo.AppearanceCustomizationMenu[characterInfos.Count]; for (int i = 0; i < characterInfos.Count; i++) { var subLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f / characterInfos.Count, 1.0f), characterInfoColumns.RectTransform)); var (characterInfo, job) = characterInfos[i]; characterInfo.CreateIcon(new RectTransform(new Vector2(1.0f, 0.275f), subLayout.RectTransform)); var jobTextContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), subLayout.RectTransform), style: null); var jobText = new GUITextBlock(new RectTransform(Vector2.One, jobTextContainer.RectTransform), job.Name, job.UIColor); var characterName = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.1f), subLayout.RectTransform)) { Text = characterInfo.Name, UserData = "random" }; characterName.OnDeselected += (sender, key) => { if (string.IsNullOrWhiteSpace(sender.Text)) { characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced); sender.Text = characterInfo.Name; sender.UserData = "random"; } else { characterInfo.Name = sender.Text; sender.UserData = "user"; } }; characterName.OnEnterPressed += (sender, text) => { sender.Deselect(); return false; }; var customizationFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f), subLayout.RectTransform), style: null); CharacterMenus[i] = new CharacterInfo.AppearanceCustomizationMenu(characterInfo, customizationFrame, hasIcon: false) { OnHeadSwitch = menu => { if (characterName.UserData is string ud && ud == "random") { characterInfo.Name = characterInfo.GetRandomName(Rand.RandSync.Unsynced); characterName.Text = characterInfo.Name; characterName.UserData = "random"; } StealRandomizeButton(menu, jobTextContainer); } }; StealRandomizeButton(CharacterMenus[i], jobTextContainer); } } private void CreateCustomizeWindow() { CampaignCustomizeSettings = new GUIMessageBox("", "", new string[] { TextManager.Get("OK") }, new Vector2(0.2f, 0.2f)); CampaignCustomizeSettings.Buttons[0].OnClicked += CampaignCustomizeSettings.Close; CampaignSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter)) { RelativeSpacing = 0.1f }; 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: GUI.Style.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", fallBackTag: "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) - 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString(); return true; } }; string 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") { OnClicked = (button, obj) => { MaxMissionCountText.Text = Math.Clamp(Int32.Parse(MaxMissionCountText.Text) + 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString(); return true; } }; maxMissionCountContainer.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip); } private static void StealRandomizeButton(CharacterInfo.AppearanceCustomizationMenu menu, GUIComponent parent) { //This is just stupid var randomizeButton = menu.RandomizeButton; var oldButton = parent.GetChild(); parent.RemoveChild(oldButton); randomizeButton.RectTransform.Parent = parent.RectTransform; randomizeButton.RectTransform.RelativeSize = Vector2.One * 1.3f; } private bool FinishSetup(GUIButton btn, object userdata) { if (string.IsNullOrWhiteSpace(saveNameBox.Text)) { saveNameBox.Flash(GUI.Style.Red); return false; } SubmarineInfo selectedSub = null; if (!(subList.SelectedData is SubmarineInfo)) { return false; } selectedSub = subList.SelectedData as SubmarineInfo; if (selectedSub.SubmarineClass == SubmarineClass.Undefined) { new GUIMessageBox(TextManager.Get("error"), TextManager.Get("undefinedsubmarineselected")); return false; } if (string.IsNullOrEmpty(selectedSub.MD5Hash.Hash)) { ((GUITextBlock)subList.SelectedComponent).TextColor = Color.DarkRed * 0.8f; subList.SelectedComponent.CanBeFocused = false; subList.Deselect(); return false; } 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, out int missionCount)) { settings.MaxMissionCount = missionCount; } else { settings.MaxMissionCount = CampaignSettings.DefaultMaxMissionCount; } if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) { if (!hasRequiredContentPackages) { var msgBox = new GUIMessageBox(TextManager.Get("ContentPackageMismatch"), TextManager.GetWithVariable("ContentPackageMismatchWarning", "[requiredcontentpackages]", string.Join(", ", selectedSub.RequiredContentPackages)), new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); msgBox.Buttons[0].OnClicked = msgBox.Close; msgBox.Buttons[0].OnClicked += (button, obj) => { if (GUIMessageBox.MessageBoxes.Count == 0) { StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); } return true; }; msgBox.Buttons[1].OnClicked = msgBox.Close; } if (selectedSub.HasTag(SubmarineTag.Shuttle)) { var msgBox = new GUIMessageBox(TextManager.Get("ShuttleSelected"), TextManager.Get("ShuttleWarning"), new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); msgBox.Buttons[0].OnClicked = (button, obj) => { StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); return true; }; msgBox.Buttons[0].OnClicked += msgBox.Close; msgBox.Buttons[1].OnClicked = msgBox.Close; return false; } } else { StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); } return true; } public void RandomizeSeed() { seedBox.Text = ToolBox.RandomSeed(8); } private void FilterSubs(GUIListBox subList, string filter) { foreach (GUIComponent child in subList.Content.Children) { var sub = child.UserData as SubmarineInfo; if (sub == null) { return; } child.Visible = string.IsNullOrEmpty(filter) || sub.DisplayName.ToLower().Contains(filter.ToLower()); } } private bool OnSubSelected(GUIComponent component, object obj) { if (subPreviewContainer == null) { return false; } (subPreviewContainer.Parent as GUILayoutGroup)?.Recalculate(); subPreviewContainer.ClearChildren(); if (!(obj is SubmarineInfo sub)) { return true; } #if !DEBUG if (sub.Price > CampaignMode.InitialMoney && !GameMain.DebugDraw) { SetPage(0); nextButton.Enabled = false; return false; } #endif nextButton.Enabled = true; sub.CreatePreviewWindow(subPreviewContainer); return true; } public void CreateDefaultSaveName() { string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Singleplayer); saveNameBox.Text = Path.GetFileNameWithoutExtension(savePath); } public void UpdateSubList(IEnumerable submarines) { List subsToShow; if (subFilter != CategoryFilter.All) { subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass && s.IsVanillaSubmarine() == (subFilter == CategoryFilter.Vanilla)).ToList(); } else { string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder); subsToShow = submarines.Where(s => s.IsCampaignCompatibleIgnoreClass && Path.GetDirectoryName(Path.GetFullPath(s.FilePath)) != downloadFolder).ToList(); } subsToShow.Sort((s1, s2) => { int p1 = s1.Price > CampaignMode.InitialMoney ? 10 : 0; int p2 = s2.Price > CampaignMode.InitialMoney ? 10 : 0; return p1.CompareTo(p2) * 100 + s1.Name.CompareTo(s2.Name); }); subList.ClearChildren(); foreach (SubmarineInfo sub in subsToShow) { var textBlock = new GUITextBlock( new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) }, ToolBox.LimitString(sub.DisplayName, GUI.Font, subList.Rect.Width - 65), style: "ListBoxElement") { ToolTip = sub.Description, UserData = sub }; if (!sub.RequiredContentPackagesInstalled) { textBlock.TextColor = Color.Lerp(textBlock.TextColor, Color.DarkRed, .5f); textBlock.ToolTip = TextManager.Get("ContentPackageMismatch") + "\n\n" + textBlock.RawToolTip; } 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: GUI.SmallFont) { TextColor = sub.Price > CampaignMode.InitialMoney ? GUI.Style.Red : textBlock.TextColor * 0.8f, ToolTip = textBlock.ToolTip }; #if !DEBUG if (!GameMain.DebugDraw) { if (sub.Price > CampaignMode.InitialMoney || !sub.IsCampaignCompatible) { textBlock.CanBeFocused = false; textBlock.TextColor *= 0.5f; } } #endif } if (SubmarineInfo.SavedSubmarines.Any()) { var validSubs = subsToShow.Where(s => s.IsCampaignCompatible && s.Price <= CampaignMode.InitialMoney).ToList(); if (validSubs.Count > 0) { subList.Select(validSubs[Rand.Int(validSubs.Count)]); } } } private List prevSaveFiles; public void UpdateLoadMenu(IEnumerable saveFiles = null) { prevSaveFiles?.Clear(); prevSaveFiles = null; loadGameContainer.ClearChildren(); if (saveFiles == null) { saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer); } var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), loadGameContainer.RectTransform), childAnchor: Anchor.TopCenter) { Stretch = true, RelativeSpacing = 0.03f }; saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform)) { OnSelected = SelectSaveFile }; new GUIButton(new RectTransform(new Vector2(0.6f, 0.08f), leftColumn.RectTransform), TextManager.Get("showinfolder")) { OnClicked = (btn, userdata) => { try { ToolBox.OpenFileWithShell(SaveUtil.SaveFolder); } catch (Exception e) { new GUIMessageBox( TextManager.Get("error"), TextManager.GetWithVariables("showinfoldererror", new string[] { "[folder]", "[errormessage]" }, new string[] { SaveUtil.SaveFolder, e.Message })); } return true; } }; foreach (string saveFile in saveFiles) { string fileName = saveFile; string subName = ""; string saveTime = ""; string contentPackageStr = ""; var saveFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), saveList.Content.RectTransform) { MinSize = new Point(0, 45) }, style: "ListBoxElement") { UserData = saveFile }; var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform), "") { CanBeFocused = false }; bool isCompatible = true; prevSaveFiles ??= new List(); nameText.Text = Path.GetFileNameWithoutExtension(saveFile); XDocument doc = SaveUtil.LoadGameSessionDoc(saveFile); if (doc?.Root == null) { DebugConsole.ThrowError("Error loading save file \"" + saveFile + "\". The file may be corrupted."); nameText.TextColor = GUI.Style.Red; continue; } if (doc.Root.GetChildElement("multiplayercampaign") != null) { //multiplayer campaign save in the wrong folder -> don't show the save saveList.Content.RemoveChild(saveFrame); continue; } subName = doc.Root.GetAttributeString("submarine", ""); saveTime = doc.Root.GetAttributeString("savetime", ""); isCompatible = SaveUtil.IsSaveFileCompatible(doc); contentPackageStr = doc.Root.GetAttributeString("selectedcontentpackages", ""); prevSaveFiles?.Add(saveFile); if (!string.IsNullOrEmpty(saveTime) && long.TryParse(saveTime, out long unixTime)) { DateTime time = ToolBox.Epoch.ToDateTime(unixTime); saveTime = time.ToString(); } if (!string.IsNullOrEmpty(contentPackageStr)) { List contentPackagePaths = contentPackageStr.Split('|').ToList(); if (!GameSession.IsCompatibleWithEnabledContentPackages(contentPackagePaths, out string errorMsg)) { nameText.TextColor = GUI.Style.Red; saveFrame.ToolTip = string.Join("\n", errorMsg, TextManager.Get("campaignmode.contentpackagemismatchwarning")); } } if (!isCompatible) { nameText.TextColor = GUI.Style.Red; saveFrame.ToolTip = TextManager.Get("campaignmode.incompatiblesave"); } new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), saveFrame.RectTransform, Anchor.BottomLeft), text: subName, font: GUI.SmallFont) { CanBeFocused = false, UserData = fileName }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), saveFrame.RectTransform), text: saveTime, textAlignment: Alignment.Right, font: GUI.SmallFont) { CanBeFocused = false, UserData = fileName }; } saveList.Content.RectTransform.SortChildren((c1, c2) => { string file1 = c1.GUIComponent.UserData as string; string file2 = c2.GUIComponent.UserData as string; DateTime file1WriteTime = DateTime.MinValue; DateTime file2WriteTime = DateTime.MinValue; try { 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 { file2WriteTime = File.GetLastWriteTime(file2); } catch { //do nothing - DateTime.MinValue will be used and the element will get sorted at the bottom of the list }; return file2WriteTime.CompareTo(file1WriteTime); }); loadGameButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomRight), TextManager.Get("LoadButton")) { OnClicked = (btn, obj) => { if (string.IsNullOrWhiteSpace(saveList.SelectedData as string)) { return false; } LoadGame?.Invoke(saveList.SelectedData as string); return true; }, Enabled = false }; } private bool SelectSaveFile(GUIComponent component, object obj) { string fileName = (string)obj; XDocument doc = SaveUtil.LoadGameSessionDoc(fileName); if (doc?.Root == null) { DebugConsole.ThrowError("Error loading save file \"" + fileName + "\". The file may be corrupted."); return false; } loadGameButton.Enabled = SaveUtil.IsSaveFileCompatible(doc); RemoveSaveFrame(); string subName = doc.Root.GetAttributeString("submarine", ""); string saveTime = doc.Root.GetAttributeString("savetime", "unknown"); if (long.TryParse(saveTime, out long unixTime)) { DateTime time = ToolBox.Epoch.ToDateTime(unixTime); saveTime = time.ToString(); } string mapseed = doc.Root.GetAttributeString("mapseed", "unknown"); var saveFileFrame = new GUIFrame(new RectTransform(new Vector2(0.45f, 0.6f), loadGameContainer.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.1f) }, style: "InnerFrame") { UserData = "savefileframe" }; var titleText = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.2f), saveFileFrame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0, 0.05f) }, Path.GetFileNameWithoutExtension(fileName), font: GUI.LargeFont, textAlignment: Alignment.Center); titleText.Text = ToolBox.LimitString(titleText.Text, titleText.Font, titleText.Rect.Width); var layoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.5f), saveFileFrame.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0, 0.1f) }); new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("Submarine")} : {subName}", font: GUI.SmallFont); new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("LastSaved")} : {saveTime}", font: GUI.SmallFont); new GUITextBlock(new RectTransform(new Vector2(1, 0), layoutGroup.RectTransform), $"{TextManager.Get("MapSeed")} : {mapseed}", font: GUI.SmallFont); new GUIButton(new RectTransform(new Vector2(0.4f, 0.15f), saveFileFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0, 0.1f) }, TextManager.Get("Delete"), style: "GUIButtonSmall") { UserData = fileName, OnClicked = DeleteSave }; return true; } private bool DeleteSave(GUIButton button, object obj) { string saveFile = obj as string; if (obj == null) { return false; } string header = TextManager.Get("deletedialoglabel"); string body = TextManager.GetWithVariable("deletedialogquestion", "[file]", Path.GetFileNameWithoutExtension(saveFile)); EventEditorScreen.AskForConfirmation(header, body, () => { SaveUtil.DeleteSave(saveFile); prevSaveFiles?.RemoveAll(s => s.StartsWith(saveFile)); UpdateLoadMenu(prevSaveFiles.ToList()); return true; }); return true; } private void RemoveSaveFrame() { GUIComponent prevFrame = null; foreach (GUIComponent child in loadGameContainer.Children) { if (child.UserData as string != "savefileframe") continue; prevFrame = child; break; } loadGameContainer.RemoveChild(prevFrame); } } }