using Microsoft.Xna.Framework; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using Barotrauma.Networking; namespace Barotrauma { class MultiPlayerCampaignSetupUI : CampaignSetupUI { private GUIButton rollbackSaveButton; private GUIButton deleteMpSaveButton; private int prevInitialMoney; private CampaignSettingElements campaignSettingElements; public bool LoadGameMenuVisible => loadGameContainer is { Visible: true }; public MultiPlayerCampaignSetupUI(GUIComponent newGameContainer, GUIComponent loadGameContainer, List saveFiles = null) : base(newGameContainer, loadGameContainer) { var verticalLayout = new GUILayoutGroup(new RectTransform(Vector2.One, newGameContainer.RectTransform), isHorizontal: false) { Stretch = true, RelativeSpacing = 0.025f }; GUILayoutGroup nameSeedLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), 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.0f }; // New game var saveLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, GUI.IntScale(24)) }, TextManager.Get("SaveName"), textAlignment: Alignment.CenterLeft); saveNameBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), saveLabel.RectTransform, Anchor.CenterRight), string.Empty) { textFilterFunction = ToolBox.RemoveInvalidFileNameChars }; saveLabel.InheritTotalChildrenMinHeight(); var seedLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.03f), nameSeedLayout.RectTransform) { MinSize = new Point(0, GUI.IntScale(24)) }, TextManager.Get("MapSeed"), textAlignment: Alignment.CenterLeft); seedBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), seedLabel.RectTransform, Anchor.CenterRight), ToolBox.RandomSeed(8)); seedLabel.InheritTotalChildrenMinHeight(); nameSeedLayout.InheritTotalChildrenMinHeight(); campaignSettingElements = CreateCampaignSettingList(campaignSettingLayout, CampaignSettings.Empty, false); var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), verticalLayout.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(30)) }, childAnchor: Anchor.BottomRight, isHorizontal: true); prevInitialMoney = CampaignSettings.DefaultInitialMoney; InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", font: GUIStyle.SmallFont, textColor: GUIStyle.Green, textAlignment: Alignment.CenterRight) { TextGetter = () => { int defaultInitialMoney = CampaignSettings.DefaultInitialMoney; int initialMoney = defaultInitialMoney; if (CampaignModePresets.TryGetAttribute( nameof(CampaignSettings.StartingBalanceAmount).ToIdentifier(), campaignSettingElements.StartingFunds.GetValue().ToIdentifier(), out var attribute)) { initialMoney = attribute.GetAttributeInt(defaultInitialMoney); } if (prevInitialMoney != initialMoney) { GameMain.NetLobbyScreen.RefreshEnabledElements(); prevInitialMoney = initialMoney; } if (GameMain.NetLobbyScreen.SelectedSub != null) { initialMoney -= GameMain.NetLobbyScreen.SelectedSub.Price; } initialMoney = Math.Max(initialMoney, 0); return TextManager.GetWithVariable("campaignstartingmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", initialMoney)); } }; verticalLayout.Recalculate(); CreateLoadMenu(saveFiles); } public bool StartGameClicked(GUIButton button, object userdata) { if (string.IsNullOrWhiteSpace(saveNameBox.Text)) { saveNameBox.Flash(GUIStyle.Red, flashDuration: 5.0f); saveNameBox.Pulsate(Vector2.One, Vector2.One * 1.2f, duration: 2.0f); newGameContainer?.Flash(GUIStyle.Red, flashDuration: 0.5f); return false; } SubmarineInfo selectedSub = null; if (GameMain.NetLobbyScreen.SelectedSub == null) { return false; } selectedSub = GameMain.NetLobbyScreen.SelectedSub; if (selectedSub.SubmarineClass == SubmarineClass.Undefined) { new GUIMessageBox(TextManager.Get("error"), TextManager.Get("undefinedsubmarineselected")); return false; } if (string.IsNullOrEmpty(selectedSub.MD5Hash.StringRepresentation)) { new GUIMessageBox(TextManager.Get("error"), TextManager.Get("nohashsubmarineselected")); return false; } string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveNameBox.Text); bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled; CampaignSettings settings = campaignSettingElements.CreateSettings(); if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) { if (!hasRequiredContentPackages) { var msgBox = new GUIMessageBox(TextManager.Get("ContentPackageMismatch"), TextManager.GetWithVariable("ContentPackageMismatchWarning", "[requiredcontentpackages]", string.Join(", ", selectedSub.RequiredContentPackages)), new LocalizedString[] { 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); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); } return true; }; msgBox.Buttons[1].OnClicked = msgBox.Close; } if (selectedSub.HasTag(SubmarineTag.Shuttle)) { var msgBox = new GUIMessageBox(TextManager.Get("ShuttleSelected"), TextManager.Get("ShuttleWarning"), new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") }); msgBox.Buttons[0].OnClicked = (button, obj) => { StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); return true; }; msgBox.Buttons[0].OnClicked += msgBox.Close; msgBox.Buttons[1].OnClicked = msgBox.Close; return false; } } else { StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); } return true; } private IEnumerable WaitForCampaignSetup() { GUI.SetCursorWaiting(); var headerText = TextManager.Get("CampaignStartingPleaseWait"); var msgBox = new GUIMessageBox(headerText, TextManager.Get("CampaignStarting"), new LocalizedString[] { TextManager.Get("Cancel") }); msgBox.Buttons[0].OnClicked = (btn, userdata) => { GameMain.NetLobbyScreen.HighlightMode(GameMain.NetLobbyScreen.SelectedModeIndex); GameMain.NetLobbyScreen.SelectMode(GameMain.NetLobbyScreen.SelectedModeIndex); GUI.ClearCursorWait(); CoroutineManager.StopCoroutines("WaitForCampaignSetup"); return true; }; msgBox.Buttons[0].OnClicked += msgBox.Close; DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, 20); while (Screen.Selected != GameMain.GameScreen && DateTime.Now < timeOut) { msgBox.Header.Text = headerText + new string('.', (int)Timing.TotalTime % 3 + 1); yield return CoroutineStatus.Running; } msgBox.Close(); GUI.ClearCursorWait(); yield return CoroutineStatus.Success; } public override void CreateLoadMenu(IEnumerable saveFiles = null) { prevSaveFiles?.Clear(); prevSaveFiles = null; loadGameContainer.ClearChildren(); if (saveFiles == null) { saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Multiplayer); } var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.85f), loadGameContainer.RectTransform), childAnchor: Anchor.TopCenter) { Stretch = true, RelativeSpacing = 0.03f }; CreateSaveFilteringHeader(leftColumn); saveList = new GUIListBox(new RectTransform(Vector2.One, leftColumn.RectTransform)) { PlaySoundOnSelect = true, OnSelected = SelectSaveFile }; foreach (CampaignMode.SaveInfo saveInfo in saveFiles) { CreateSaveElement(saveInfo); } SortSaveList(); loadGameButton = new GUIButton(new RectTransform(new Vector2(0.45f, 0.12f), loadGameContainer.RectTransform, Anchor.BottomRight), TextManager.Get("LoadButton")) { OnClicked = (btn, obj) => { if (saveList.SelectedData is not CampaignMode.SaveInfo saveInfo) { return false; } if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; } if (saveInfo.RespawnMode != RespawnMode.None && saveInfo.RespawnMode != GameMain.NetworkMember?.ServerSettings?.RespawnMode) { var msgBox = new GUIMessageBox(TextManager.Get("Warning"), TextManager.GetWithVariables("RespawnModeMismatch", ("[currentrespawnmode]", TextManager.Get($"respawnmode.{GameMain.NetworkMember?.ServerSettings?.RespawnMode}")), ("[savedrespawnmode]", TextManager.Get($"respawnmode.{saveInfo.RespawnMode}"))), new LocalizedString[] { TextManager.Get("RespawnModeMismatch.GoBack"), TextManager.Get("RespawnModeMismatch.LoadAnyway") }); msgBox.Buttons[0].OnClicked = (button, obj) => { msgBox.Close(); return true; }; msgBox.Buttons[1].OnClicked = (button, obj) => { msgBox.Close(); LoadSaveGame(); return true; }; return false; } LoadSaveGame(); return true; void LoadSaveGame() { LoadGame?.Invoke(saveInfo.FilePath, backupIndex: Option.None); CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); } }, Enabled = false }; GUILayoutGroup leftButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.15f), loadGameContainer.RectTransform, Anchor.BottomLeft)) { RelativeSpacing = 0.05f, Stretch = true }; rollbackSaveButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), leftButtonContainer.RectTransform), TextManager.Get("rollbackbutton"), style: "GUIButtonSmallFreeScale") { Visible = false, ToolTip = TextManager.Get("backuptooltip"), OnClicked = ViewBackupSaveMenu }; deleteMpSaveButton = new GUIButton(new RectTransform(new Vector2(1f, 0.5f), leftButtonContainer.RectTransform), TextManager.Get("Delete"), style: "GUIButtonSmallFreeScale") { OnClicked = DeleteSave, Visible = false }; } private bool ViewBackupSaveMenu(GUIButton button, object obj) { if (obj is not CampaignMode.SaveInfo saveInfo) { return false; } if (string.IsNullOrWhiteSpace(saveInfo.FilePath)) { return false; } if (GameMain.Client.IsServerOwner) { CreateBackupMenu(SaveUtil.GetIndexData(saveInfo.FilePath), index => { LoadGame(saveInfo.FilePath, backupIndex: Option.Some(index.Index)); }); } else { RequestBackupIndexData(saveInfo.FilePath); } return true; } private const string PleaseWaitUserData = "PleaseWaitPopup"; private void RequestBackupIndexData(string savePath) { if (GameMain.Client == null) { return; } GUI.SetCursorWaiting(); var msgBox = new GUIMessageBox(TextManager.Get("CampaignStartingPleaseWait"), TextManager.Get("CampaignStarting"), new[] { TextManager.Get("Cancel") }) { UserData = PleaseWaitUserData }; msgBox.Buttons[0].OnClicked = (btn, obj) => { GUI.ClearCursorWait(); return true; }; IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.REQUEST_BACKUP_INDICES); msg.WriteString(savePath); GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable); } public void OnBackupIndicesReceived(IReadMessage message) { GUI.ClearCursorWait(); foreach (GUIComponent component in GUIMessageBox.MessageBoxes.Where(static mb => mb.UserData is PleaseWaitUserData).ToArray()) { if (component is GUIMessageBox msgBox) { msgBox.Close(); } } string path = message.ReadString(); var indexData = INetSerializableStruct.Read>(message); CreateBackupMenu(indexData, selectedIndex => { LoadGame?.Invoke(path, backupIndex: Option.Some(selectedIndex.Index)); }); } private bool SelectSaveFile(GUIComponent component, object obj) { if (obj is not CampaignMode.SaveInfo saveInfo) { return true; } string fileName = saveInfo.FilePath; loadGameButton.Enabled = true; rollbackSaveButton.Visible = true; deleteMpSaveButton.Visible = deleteMpSaveButton.Enabled = GameMain.Client.IsServerOwner; rollbackSaveButton.Enabled = deleteMpSaveButton.Enabled = GameMain.GameSession?.DataPath.LoadPath != fileName; if (deleteMpSaveButton.Visible) { deleteMpSaveButton.UserData = saveInfo; } if (rollbackSaveButton.Visible) { rollbackSaveButton.UserData = saveInfo; } return true; } } }