7 using Microsoft.Xna.Framework;
8 using Microsoft.Xna.Framework.Graphics;
11 using System.Collections.Generic;
12 using System.Collections.Immutable;
13 using System.Diagnostics;
17 using System.Threading;
18 using System.Threading.Tasks;
19 using System.Xml.Linq;
43 private readonly Dictionary<Tab, GUIFrame> menuTabs;
47 private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
48 private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
49 private GUIDropDown languageDropdown, serverExecutableDropdown;
50 private readonly
GUIButton joinServerButton, hostServerButton;
52 private readonly
GUIFrame modsButtonContainer;
53 private readonly
GUIButton modsButton, modUpdatesButton;
54 private (DateTime WhenToRefresh,
int Count) modUpdateStatus = (DateTime.Now, 0);
55 private static readonly TimeSpan ModUpdateInterval = TimeSpan.FromSeconds(60.0f);
62 private static string RemoteContentUrl => GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
65 private XDocument remoteContentDoc;
67 private Tab selectedTab = Tab.Empty;
69 private Sprite backgroundSprite;
78 private GUITextBlock tutorialHeader, tutorialDescription;
91 leftTextFooterLayout = createTextFooter();
92 rightTextFooterLayout = createTextFooter();
94 gameAnalyticsStatusText = createLeftText(TextManager.Get($
"GameAnalyticsStatus.{GameAnalyticsManager.Consent.Unknown}"));
95 createLeftText(
"Barotrauma v" +
GameMain.
Version +
" (" + AssemblyInfo.BuildString +
", branch " + AssemblyInfo.GitBranch +
", revision " + AssemblyInfo.GitRevision +
")");
97 var privacyPolicyText = createRightText(TextManager.Get(
"privacypolicy").Fallback(
"Privacy policy"));
98 (
Rectangle Rect,
bool MouseOn) getPrivacyPolicyHoverRect()
100 var textSize = privacyPolicyText.Font.MeasureString(privacyPolicyText.Text);
101 var bottomRight = privacyPolicyText.Rect.Location.ToVector2()
102 + privacyPolicyText.TextPos
103 + privacyPolicyText.TextOffset;
104 var rect =
new Rectangle((bottomRight - textSize).ToPoint(), textSize.ToPoint());
106 return (rect, mouseOn);
109 onUpdate: (dt, component) =>
111 var (_, mouseOn) = getPrivacyPolicyHoverRect();
112 if (mouseOn && PlayerInput.PrimaryMouseButtonClicked())
114 GameMain.ShowOpenUriPrompt(
"https://privacypolicy.daedalic.com");
117 onDraw: (sb, component) =>
119 var (rect, mouseOn) = getPrivacyPolicyHoverRect();
120 Color color = mouseOn ? Color.White : Color.White * 0.7f;
121 privacyPolicyText.TextColor = color;
122 GUI.DrawLine(sb,
new Vector2(rect.Left, rect.Bottom),
new Vector2(rect.Right, rect.Bottom), color);
125 createRightText(
"© " + DateTime.Now.Year +
" Undertow Games & FakeFish. All rights reserved.");
126 createRightText(
"© " + DateTime.Now.Year +
" Daedalic Entertainment GmbH. The Daedalic logo is a trademark of Daedalic Entertainment GmbH, Germany. All rights reserved.");
128 GUILayoutGroup createTextFooter()
131 ChildAnchor =
Anchor.BottomLeft
134 GUITextBlock createTextInFooter(GUILayoutGroup footer, LocalizedString str, Alignment textAlignment)
136 var textBlock =
new GUITextBlock(
137 rectT:
new RectTransform((1.0f, 0.3f), footer.RectTransform),
139 textAlignment: textAlignment,
140 font: GUIStyle.SmallFont,
141 textColor: Color.White * 0.7f);
142 textBlock.RectTransform.SetAsFirstChild();
146 GUITextBlock createLeftText(LocalizedString str)
147 => createTextInFooter(leftTextFooterLayout, str, Alignment.BottomLeft);
148 GUITextBlock createRightText(LocalizedString str)
149 => createTextInFooter(rightTextFooterLayout, str, Alignment.BottomRight);
151 GameMain.Instance.ResolutionChanged += () =>
153 SetMenuTabPositioning();
154 CreateHostServerFields();
155 bool prevMenuOpen = GUI.SettingsMenuOpen;
156 SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
157 GUI.SettingsMenuOpen = prevMenuOpen;
158 if (remoteContentDoc?.Root !=
null)
160 remoteContentContainer.ClearChildren();
163 foreach (var subElement
in remoteContentDoc.Root.Elements())
165 GUIComponent.FromXML(subElement.FromPackage(
null), remoteContentContainer.RectTransform);
171 DebugConsole.ThrowError(
"Reading received remote main menu content failed.", e);
173 GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.RemoteContentParse:Exception", GameAnalyticsManager.ErrorSeverity.Error,
174 "Reading received remote main menu content failed. " + e.Message);
179 versionMismatchWarning =
new GUIFrame(
new RectTransform(
new Vector2(0.7f, 0.065f),
Frame.
RectTransform) { AbsoluteOffset = new Point(GUI.IntScale(15)) }, style:
"InnerFrame", color: GUIStyle.Red)
181 IgnoreLayoutGroups =
true,
184 var versionMismatchContent =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.9f), versionMismatchWarning.
RectTransform,
Anchor.Center), isHorizontal:
true)
186 RelativeSpacing = 0.05f,
188 new GUIImage(
new RectTransform(
new Vector2(1.0f), versionMismatchContent.RectTransform, scaleBasis:
ScaleBasis.Smallest), style:
"GUINotificationButton")
190 Color = GUIStyle.Orange
192 new GUITextBlock(
new RectTransform(
new Vector2(0.85f, 1.0f), versionMismatchContent.RectTransform),
193 TextManager.GetWithVariables(
"versionmismatchwarning",
194 (
"[gameversion]", GameMain.Version.ToString()),
195 (
"[contentversion]", ContentPackageManager.VanillaCorePackage.GameVersion.ToString())),
198 TextColor = GUIStyle.Orange
202 { RelativeOffset = new Vector2(0.08f, 0.05f), AbsoluteOffset = new Point(-8, -8) },
205 Color = Color.Black * 0.5f,
209 { RelativeOffset = new Vector2(0.08f, 0.05f) },
212 buttonsParent =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.3f, 0.85f), parent:
Frame.
RectTransform, anchor:
Anchor.CenterLeft)
214 AbsoluteOffset = new Point(50, 0)
218 RelativeSpacing = 0.02f
221 remoteContentContainer =
new GUIFrame(
new RectTransform(Vector2.One, parent:
Frame.
RectTransform), style:
null)
226 #if TEST_REMOTE_CONTENT
228 var doc = XMLExtensions.TryLoadXml(
"Content/UI/MenuContent.xml");
229 if (doc?.Root !=
null)
231 foreach (var subElement
in doc?.Root.Elements())
233 GUIComponent.FromXML(subElement.FromPackage(
null), remoteContentContainer.RectTransform);
237 SpamServerFilters.RequestGlobalSpamFilter();
238 FetchRemoteContent();
241 float labelHeight = 0.18f;
245 var campaignHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal:
true);
247 new GUIImage(
new RectTransform(
new Vector2(0.2f, 0.7f), campaignHolder.RectTransform),
"MainMenuCampaignIcon")
253 new GUIFrame(
new RectTransform(
new Vector2(0.02f, 0.0f), campaignHolder.RectTransform), style:
null);
255 var campaignNavigation =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.75f, 0.75f), parent: campaignHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
257 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, labelHeight), campaignNavigation.RectTransform),
258 TextManager.Get(
"CampaignLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style:
"MainMenuGUITextBlock") {
ForceUpperCase =
ForceUpperCase.Yes };
260 var campaignButtons =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 1.0f), parent: campaignNavigation.RectTransform), style:
"MainMenuGUIFrame");
262 var campaignList =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 0.2f), parent: campaignButtons.RectTransform))
265 RelativeSpacing = 0.035f
268 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get(
"TutorialButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
271 UserData = Tab.Tutorials,
272 OnClicked = (tb, userdata) =>
274 SelectTab(tb, userdata);
279 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get(
"LoadGameButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
282 UserData = Tab.LoadGame,
283 OnClicked = (tb, userdata) =>
285 SelectTab(tb, userdata);
290 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get(
"NewGameButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
293 UserData = Tab.NewGame,
294 OnClicked = (tb, userdata) =>
296 SelectTab(tb, userdata);
302 var multiplayerHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal:
true);
304 new GUIImage(
new RectTransform(
new Vector2(0.2f, 0.7f), multiplayerHolder.RectTransform),
"MainMenuMultiplayerIcon")
310 new GUIFrame(
new RectTransform(
new Vector2(0.02f, 0.0f), multiplayerHolder.RectTransform), style:
null);
312 var multiplayerNavigation =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.75f, 0.75f), parent: multiplayerHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
314 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, labelHeight), multiplayerNavigation.RectTransform),
315 TextManager.Get(
"MultiplayerLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style:
"MainMenuGUITextBlock") {
ForceUpperCase =
ForceUpperCase.Yes };
317 var multiplayerButtons =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 1.0f), parent: multiplayerNavigation.RectTransform), style:
"MainMenuGUIFrame");
319 var multiplayerList =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 0.2f), parent: multiplayerButtons.RectTransform))
322 RelativeSpacing = 0.035f
325 joinServerButton =
new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), multiplayerList.RectTransform), TextManager.Get(
"JoinServerButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
328 UserData = Tab.JoinServer,
329 OnClicked = (tb, userdata) =>
331 SelectTab(tb, userdata);
335 hostServerButton =
new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), multiplayerList.RectTransform), TextManager.Get(
"HostServerButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
338 UserData = Tab.HostServer,
339 OnClicked = (tb, userdata) =>
341 SelectTab(tb, userdata);
347 var customizeHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal:
true);
349 new GUIImage(
new RectTransform(
new Vector2(0.2f, 0.7f), customizeHolder.RectTransform),
"MainMenuCustomizeIcon")
355 new GUIFrame(
new RectTransform(
new Vector2(0.02f, 0.0f), customizeHolder.RectTransform), style:
null);
357 var customizeNavigation =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.75f, 0.75f), parent: customizeHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
359 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, labelHeight), customizeNavigation.RectTransform),
360 TextManager.Get(
"CustomizeLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style:
"MainMenuGUITextBlock") {
ForceUpperCase =
ForceUpperCase.Yes };
362 var customizeButtons =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 1.0f), parent: customizeNavigation.RectTransform), style:
"MainMenuGUIFrame");
364 var customizeList =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 0.2f), parent: customizeButtons.RectTransform))
367 RelativeSpacing = 0.035f
370 modsButtonContainer =
new GUIFrame(
new RectTransform(Vector2.One, customizeList.RectTransform),
373 modsButton =
new GUIButton(
new RectTransform(Vector2.One, modsButtonContainer.RectTransform),
374 TextManager.Get(
"settingstab.mods"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
379 OnClicked = SelectTab
382 modUpdatesButton =
new GUIButton(
new RectTransform(Vector2.One * 0.95f, modsButtonContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
383 style:
"GUIUpdateButton")
385 ToolTip = TextManager.Get(
"ModUpdatesAvailable"),
386 OnClicked = (_, _) =>
388 BulkDownloader.PrepareUpdates();
394 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get(
"SubEditorButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
397 UserData = Tab.SubmarineEditor,
398 OnClicked = (tb, userdata) =>
400 SelectTab(tb, userdata);
405 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get(
"CharacterEditorButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
408 UserData = Tab.CharacterEditor,
409 OnClicked = (tb, userdata) =>
411 SelectTab(tb, userdata);
417 var optionHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.8f), parent: buttonsParent.RectTransform), isHorizontal:
true);
419 new GUIImage(
new RectTransform(
new Vector2(0.15f, 0.6f), optionHolder.RectTransform),
"MainMenuOptionIcon")
425 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 0.0f), optionHolder.RectTransform), style:
null);
427 var optionButtons =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.0f) });
429 var optionList =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.8f, 0.25f), parent: optionButtons.RectTransform))
432 RelativeSpacing = 0.035f
435 var settingsButtonContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 1.0f), optionList.RectTransform), style:
null);
437 new GUIButton(
new RectTransform(Vector2.One, settingsButtonContainer.RectTransform), TextManager.Get(
"SettingsButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
440 UserData = Tab.Settings,
441 OnClicked = SelectTab
444 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get(
"EditorDisclaimerWikiLink"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
447 OnClicked = (button, userData) =>
449 string url = TextManager.Get(
"EditorDisclaimerWikiUrl").Fallback(
"https://barotraumagame.com/wiki").Value;
450 GameMain.ShowOpenUriPrompt(url, promptExtensionTag:
"wikinotice");
454 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get(
"CreditsButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
457 UserData = Tab.Credits,
458 OnClicked = SelectTab
460 new GUIButton(
new RectTransform(
new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get(
"QuitButton"), textAlignment: Alignment.Left, style:
"MainMenuGUIButton")
463 OnClicked = QuitClicked
468 new GUIButton(
new RectTransform(
new Point(300, 30),
Frame.
RectTransform,
Anchor.TopRight) { AbsoluteOffset = new Point(40, 80) },
469 "Quickstart (dev)", style:
"GUIButtonLarge", color: GUIStyle.Red)
471 IgnoreLayoutGroups =
true,
472 UserData = Tab.Empty,
473 OnClicked = (tb, userdata) =>
475 SelectTab(tb, userdata);
482 new GUIButton(
new RectTransform(
new Point(300, 30),
Frame.
RectTransform,
Anchor.TopRight) { AbsoluteOffset = new Point(40, 130) },
483 "Profiling", style:
"GUIButtonLarge", color: GUIStyle.Red)
485 IgnoreLayoutGroups =
true,
486 UserData = Tab.Empty,
487 ToolTip =
"Enables performance indicators and starts the game with a fixed sub, crew and level to make it easier to compare the performance between sessions.",
488 OnClicked = (tb, userdata) =>
490 SelectTab(tb, userdata);
493 GameMain.ShowPerf =
true;
494 GameMain.ShowFPS =
true;
499 new GUIButton(
new RectTransform(
new Point(300, 30),
Frame.
RectTransform,
Anchor.TopRight) { AbsoluteOffset = new Point(40, 180) },
500 "Join Localhost", style:
"GUIButtonLarge", color: GUIStyle.Red)
502 IgnoreLayoutGroups =
true,
503 UserData = Tab.Empty,
504 ToolTip =
"Connects to a locally hosted dedicated server, assuming default port.",
505 OnClicked = (tb, userdata) =>
507 SelectTab(tb, userdata);
509 GameMain.Client =
new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
510 new LidgrenEndpoint(IPAddress.Loopback, NetConfig.DefaultPort),
"localhost", Option<int>.None());
516 new GUIButton(
new RectTransform(
new Point(300, 30),
Frame.
RectTransform,
Anchor.TopLeft) { AbsoluteOffset = new Point(40, 50) },
517 $
"Open LuaCs Settings", style:
"MainMenuGUIButton", color: GUIStyle.Red)
519 IgnoreLayoutGroups =
true,
520 OnClicked = (tb, userdata) =>
527 string version = File.Exists(LuaCsSetup.VersionFile) ? File.ReadAllText(LuaCsSetup.VersionFile) :
"Github";
529 new GUITextBlock(
new RectTransform(
new Point(300, 30),
Frame.
RectTransform,
Anchor.TopLeft) { AbsoluteOffset = new Point(10, 10) }, $
"Using LuaCsForBarotrauma revision {AssemblyInfo.GitRevision} version {version}", Color.Red)
531 IgnoreLayoutGroups =
false
534 var minButtonSize =
new Point(120, 20);
535 var maxButtonSize =
new Point(480, 80);
537 var relativeSize =
new Vector2(0.6f, 0.65f);
538 var minSize =
new Point(600, 400);
539 var maxSize =
new Point(2000, 1500);
540 var anchor =
Anchor.Center;
541 var pivot =
Pivot.Center;
542 Vector2 relativeOffset =
new Vector2(0.05f, 0.0f);
544 menuTabs =
new Dictionary<Tab, GUIFrame>
546 [Tab.Settings] =
new GUIFrame(
new RectTransform(
new Vector2(relativeSize.X, 0.8f),
Frame.
RectTransform, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset },
551 [Tab.NewGame] =
new GUIFrame(
new RectTransform(relativeSize *
new Vector2(1.0f, 1.15f),
Frame.
RectTransform, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset }),
552 [Tab.LoadGame] =
new GUIFrame(
new RectTransform(relativeSize,
Frame.
RectTransform, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset })
555 CreateCampaignSetupUI();
557 var hostServerScale =
new Vector2(0.7f, 1.2f);
558 menuTabs[Tab.HostServer] =
new GUIFrame(
new RectTransform(
559 Vector2.Multiply(relativeSize, hostServerScale),
Frame.
RectTransform, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))
560 { RelativeOffset = relativeOffset });
562 CreateHostServerFields();
566 menuTabs[Tab.Tutorials] =
new GUIFrame(
new RectTransform(relativeSize,
Frame.
RectTransform, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset });
571 menuTabs[Tab.Credits] =
new GUIFrame(
new RectTransform(Vector2.One,
Frame.
RectTransform,
Anchor.Center), style:
null)
575 var blockerFrame =
new GUIFrame(
new RectTransform(GUI.Canvas.RelativeSize, menuTabs[Tab.Credits].RectTransform,
Anchor.Center), style:
"GUIBackgroundBlocker")
579 blockerFrame.RectTransform.RelativeOffset = GUI.IsUltrawide ? Vector2.Zero :
new Vector2(0.05f, 0.0f);
581 var creditsContainer =
new GUIFrame(
new RectTransform(
new Vector2(0.75f, 1.5f), menuTabs[Tab.Credits].RectTransform,
Anchor.CenterRight), style:
"OuterGlow", color: Color.Black * 0.8f);
582 creditsPlayer =
new CreditsPlayer(
new RectTransform(Vector2.One, creditsContainer.RectTransform),
"Content/Texts/Credits.xml");
583 creditsPlayer.CloseButton.OnClicked = (btn, userdata) =>
585 SelectTab(Tab.Empty);
589 SetMenuTabPositioning();
590 SelectTab(Tab.Empty);
593 private void SetMenuTabPositioning()
595 foreach (GUIFrame menuTab
in menuTabs.Values)
597 var anchor = GUI.IsUltrawide ?
Anchor.Center :
Anchor.CenterRight;
598 var pivot = GUI.IsUltrawide ?
Pivot.Center :
Pivot.CenterRight;
599 Vector2 relativeOffset = GUI.IsUltrawide ? Vector2.Zero :
new Vector2(0.05f, 0.0f);
600 menuTab.RectTransform.SetPosition(anchor, pivot);
601 menuTab.RectTransform.RelativeOffset = relativeOffset;
605 private void CreateTutorialTab()
607 var tutorialInnerFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.9f, 0.9f), menuTabs[Tab.Tutorials].RectTransform,
Anchor.Center), style:
"InnerFrame");
608 var tutorialContent =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.95f), tutorialInnerFrame.RectTransform,
Anchor.Center), isHorizontal:
true) { RelativeSpacing = 0.02f, Stretch =
true };
610 tutorialList =
new GUIListBox(
new RectTransform(
new Vector2(0.4f, 1.0f), tutorialContent.RectTransform))
612 PlaySoundOnSelect =
true,
613 OnSelected = (component, obj) =>
619 var tutorialPreview =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.6f, 1.0f), tutorialContent.RectTransform)) { RelativeSpacing = 0.05f, Stretch =
true };
620 var imageContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style:
"InnerFrame");
621 tutorialBanner =
new GUIImage(
new RectTransform(Vector2.One, imageContainer.RectTransform), style:
null, scaleToFit:
true);
623 var infoContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style:
"GUIFrameListBox");
624 var infoContent =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.9f), infoContainer.RectTransform,
Anchor.Center), childAnchor:
Anchor.TopLeft)
626 AbsoluteSpacing = GUI.IntScale(10)
629 tutorialHeader =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), infoContent.RectTransform),
string.Empty, font: GUIStyle.SubHeadingFont);
630 tutorialDescription =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), infoContent.RectTransform),
string.Empty, wrap:
true);
632 var startButton =
new GUIButton(
new RectTransform(
new Vector2(0.5f, 0.0f), infoContent.RectTransform,
Anchor.BottomRight), text: TextManager.Get(
"startgamebutton"))
634 IgnoreLayoutGroups =
true,
635 OnClicked = (component, obj) =>
643 foreach (var tutorialPrefab
in TutorialPrefab.Prefabs.OrderBy(p => p.Order))
645 var tutorial =
new Tutorial(tutorialPrefab);
646 firstTutorial ??= tutorial;
647 var tutorialText =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), tutorialList.
Content.
RectTransform), tutorial.DisplayName)
649 Padding =
new Vector4(30.0f * GUI.Scale, 0,0,0),
652 tutorialText.RectTransform.MinSize =
new Point(0, (
int)(tutorialText.TextSize.Y * 2));
654 GUITextBlock.AutoScaleAndNormalize(tutorialList.
Content.
Children.Select(c => c as GUITextBlock));
655 tutorialList.
Select(firstTutorial);
658 private void SelectTutorial(
Tutorial tutorial)
664 (tutorialDescription.
Parent as GUILayoutGroup)?.Recalculate();
673 menuScreen.tutorialList.ClearChildren();
674 menuScreen.CreateTutorialTab();
684 ResetModUpdateButton();
686 if (WorkshopItemsToUpdate.Any())
688 while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
690 SteamManager.Workshop.OnItemDownloadComplete(workshopId, forceInstall:
true);
694 GUI.PreventPauseMenuToggle =
false;
709 ResetButtonStates(
null);
711 Eos.EosAccount.ExecuteAfterLogin(AchievementManager.SyncBetweenPlatforms);
720 private bool SelectTab(
GUIButton button,
object obj)
722 titleText.Visible =
true;
729 SelectTab(Tab.Empty);
734 private bool SelectTab(Tab tab)
736 titleText.Visible =
true;
737 SettingsMenu.Instance?.Close();
738 #warning TODO: reimplement settings confirmation dialog
743 if (GameSettings.CurrentConfig.TutorialSkipWarning)
745 selectedTab = Tab.Empty;
746 ShowTutorialSkipWarning(Tab.NewGame);
753 campaignSetupUI.
UpdateSubList(SubmarineInfo.SavedSubmarines);
759 SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
762 if (GameSettings.CurrentConfig.TutorialSkipWarning)
764 selectedTab = Tab.Empty;
765 ShowTutorialSkipWarning(Tab.JoinServer);
768 GameMain.ServerListScreen.Select();
771 if (GameSettings.CurrentConfig.TutorialSkipWarning)
773 selectedTab = Tab.Empty;
774 ShowTutorialSkipWarning(tab);
778 .Where(c => c.UserData is ServerExecutableFile f && !ContentPackageManager.EnabledPackages.All.Contains(f.ContentPackage))
781 = ContentPackageManager.EnabledPackages.All.SelectMany(p => p.GetFiles<ServerExecutableFile>())
784 foreach (var newServerExe
in newServerExes)
786 var serverExeEntry = serverExecutableDropdown.
AddItem($
"{newServerExe.ContentPackage.Name} - {Path.GetFileNameWithoutExtension(newServerExe.Path.Value)}", userData: newServerExe);
787 if (newServerExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
790 TextManager.GetWithVariables(
"versionmismatchwarning",
791 (
"[gameversion]", newServerExe.ContentPackage.GameVersion.ToString()),
792 (
"[contentversion]", GameMain.VanillaContent.GameVersion.ToString()));
793 if (serverExeEntry is GUITextBlock serverExeText)
795 serverExeText.TextColor = GUIStyle.Red;
801 c.RectTransform.RelativeSize = (1.0f, c.RectTransform.RelativeSize.Y);
802 c.ForceLayoutRecalculation();
806 if (wasPickable != serverExePickable)
810 (serverExecutableDropdown.
Parent.
Parent as GUILayoutGroup)?.Recalculate();
813 serverExecutableDropdown.
Select(0);
818 UpdateTutorialList();
820 case Tab.CharacterEditor:
822 CoroutineManager.StartCoroutine(SelectScreenWithWaitCursor(GameMain.CharacterEditorScreen));
824 case Tab.SubmarineEditor:
825 CoroutineManager.StartCoroutine(SelectScreenWithWaitCursor(GameMain.SubEditorScreen));
828 var settings = SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
829 settings.SelectTab(SettingsMenu.Tab.Mods);
833 titleText.Visible =
false;
834 creditsPlayer.Restart();
837 titleText.Visible =
true;
838 selectedTab = Tab.Empty;
843 leftTextFooterLayout.Visible = tab != Tab.Credits;
844 rightTextFooterLayout.Visible = tab != Tab.Credits;
846 foreach (var tabFrame
in menuTabs.Values)
848 tabFrame.Visible =
false;
850 if (menuTabs.TryGetValue(selectedTab, out var visibleTab)) { visibleTab.Visible =
true; }
855 private IEnumerable<CoroutineStatus> SelectScreenWithWaitCursor(Screen screen)
857 GUI.SetCursorWaiting();
859 yield
return new WaitForSeconds(0.02f);
860 GUI.ClearCursorWait();
862 yield
return CoroutineStatus.Success;
867 GUI.PreventPauseMenuToggle =
false;
875 ResetButtonStates(button);
883 private void ResetButtonStates(
GUIButton button)
888 if (otherButton ==
null || otherButton == button)
continue;
897 modUpdateStatus = (DateTime.Now, 0);
898 modUpdatesButton.Visible =
false;
905 Rand.SetSyncedSeed(1);
906 Rand.SetLocalRandom(1);
910 Identifier subName = sub.IfEmpty(GameSettings.CurrentConfig.QuickStartSub);
911 if (!subName.IsEmpty)
913 DebugConsole.NewMessage($
"Loading the predefined quick start sub \"{subName}\"", Color.White);
916 if (selectedSub ==
null)
918 DebugConsole.NewMessage($
"Cannot find a sub that matches the name \"{subName}\".", Color.Red);
921 if (selectedSub ==
null)
923 DebugConsole.NewMessage(
"Loading a random sub.", Color.White);
925 selectedSub = subs.ElementAt(Rand.Int(subs.Count()));
930 missionPrefabs:
null);
932 gamesession.StartRound(fixedSeed ?
"abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams);
935 Identifier[] jobIdentifiers =
new Identifier[] {
936 "captain".ToIdentifier(),
937 "engineer".ToIdentifier(),
938 "mechanic".ToIdentifier(),
939 "securityofficer".ToIdentifier(),
940 "medicaldoctor".ToIdentifier() };
941 foreach (Identifier job
in jobIdentifiers)
944 var variant = Rand.Range(0, jobPrefab.Variants);
946 if (characterInfo.Job ==
null)
948 DebugConsole.ThrowError(
"Failed to find the job \"" + job +
"\"!");
950 gamesession.CrewManager.AddCharacterInfo(characterInfo);
952 gamesession.CrewManager.InitSinglePlayerRound();
955 private void ShowTutorialSkipWarning(Tab tabToContinueTo)
957 var tutorialSkipWarning =
new GUIMessageBox(
"", TextManager.Get(
"tutorialskipwarning"),
new LocalizedString[] { TextManager.Get(
"tutorialwarningskiptutorials"), TextManager.Get(
"tutorialwarningplaytutorials") });
960 => (btn, userdata) =>
962 var config = GameSettings.CurrentConfig;
963 config.TutorialSkipWarning =
false;
964 GameSettings.SetCurrentConfig(config);
965 GameSettings.SaveCurrentConfig();
966 tutorialSkipWarning.Close();
971 tutorialSkipWarning.Buttons[0].OnClicked += proceedToTab(tabToContinueTo);
972 tutorialSkipWarning.Buttons[1].OnClicked += proceedToTab(Tab.Tutorials);
977 base.AddToGUIUpdateList();
981 campaignSetupUI.
CharacterMenus?.ForEach(
static m => m.AddToGUIUpdateList());
986 private void UpdateTutorialList()
998 private bool ChangeMaxPlayers(GUIButton button,
object obj)
1000 int.TryParse(maxPlayersBox.
Text, out
int currMaxPlayers);
1001 currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers + (
int)button.UserData, 1, NetConfig.MaxPlayers);
1002 maxPlayersBox.
Text = currMaxPlayers.ToString();
1006 private void TryStartServer()
1008 if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
1010 var waitBox =
new GUIMessageBox(TextManager.Get(
"pleasewait"), TextManager.Get(
"waitforsubmarinehashcalculations"),
new LocalizedString[] { TextManager.Get(
"cancel") });
1011 var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox),
"WaitForSubmarineHashCalculations");
1012 waitBox.Buttons[0].OnClicked += (btn, userdata) =>
1014 CoroutineManager.StopCoroutines(waitCoroutine);
1024 private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
1026 LocalizedString originalText = messageBox.Text.Text;
1030 doneCount = SubmarineInfo.SavedSubmarines.Count(s => !s.CalculatingHash);
1031 messageBox.Text.Text = originalText + $
" ({doneCount}/{SubmarineInfo.SavedSubmarines.Count()})";
1032 yield
return CoroutineStatus.Running;
1033 }
while (doneCount < SubmarineInfo.SavedSubmarines.Count());
1036 yield
return CoroutineStatus.Success;
1039 private void StartServer()
1041 string name = serverNameBox.
Text;
1043 GameMain.ResetNetLobbyScreen();
1048 f.ContentPackage != GameMain.VanillaContent)
1050 fileName = Path.Combine(
1051 Path.GetDirectoryName(f.Path.Value),
1052 Path.GetFileNameWithoutExtension(f.Path.Value));
1060 fileName =
"DedicatedServer.exe";
1062 fileName =
"./DedicatedServer";
1066 var arguments =
new List<string>
1069 "-public", isPublicBox.
Selected.ToString(),
1071 "-banafterwrongpassword", wrongPasswordBanBox.
Selected.ToString(),
1072 "-karmaenabled", (!karmaBox.
Selected).ToString(),
1073 "-maxplayers", maxPlayersBox.
Text,
1077 if (!
string.IsNullOrWhiteSpace(passwordBox.
Text))
1079 arguments.Add(
"-password");
1080 arguments.Add(passwordBox.
Text);
1084 arguments.Add(
"-nopassword");
1087 var puids = EosInterface.IdQueries.GetLoggedInPuids();
1089 var endpoints =
new List<Endpoint>();
1090 if (SteamManager.GetSteamId().TryUnwrap(out var steamId))
1094 if (puids.Length > 0)
1098 if (endpoints.Count == 0)
1100 endpoints.Add(
new LidgrenEndpoint(IPAddress.Loopback, NetConfig.DefaultPort));
1103 if (endpoints.First() is
P2PEndpoint firstEndpoint)
1105 arguments.Add(
"-endpoint");
1106 arguments.Add(firstEndpoint.StringRepresentation);
1108 int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
1109 arguments.Add(
"-ownerkey");
1110 arguments.Add(ownerKey.ToString());
1112 var processInfo =
new ProcessStartInfo
1114 FileName = fileName,
1115 WorkingDirectory = Directory.GetCurrentDirectory(),
1117 CreateNoWindow =
true,
1118 UseShellExecute =
false,
1119 WindowStyle = ProcessWindowStyle.Hidden
1122 arguments.ForEach(processInfo.ArgumentList.Add);
1123 ChildServerRelay.Start(processInfo);
1126 GameMain.Client =
new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(
1127 SteamManager.GetUsername().FallbackNullOrEmpty(name)),
1128 endpoints.ToImmutableArray(),
1130 Option.Some(ownerKey));
1134 DebugConsole.ThrowError(
"Failed to start server", e);
1138 private bool QuitClicked(GUIButton button,
object obj)
1144 private void UpdateOutOfDateWorkshopItemCount()
1146 if (DateTime.Now < modUpdateStatus.WhenToRefresh) {
return; }
1147 if (!SteamManager.IsInitialized) {
return; }
1149 var installedPackages = ContentPackageManager.WorkshopPackages;
1151 var ids = SteamManager.Workshop.GetSubscribedItemIds()
1152 .Select(
id =>
id.Value)
1153 .Union(installedPackages
1154 .
Select(pkg => pkg.UgcId)
1156 .OfType<SteamWorkshopId>()
1157 .Select(
id =>
id.Value));
1161 .Select(
id =>
new Steamworks.Ugc.Item(
id))
1163 installedPackages.FirstOrDefault(p
1164 => p.UgcId.TryUnwrap(out SteamWorkshopId
id) &&
id.Value == item.Id)
1169 && (item.IsDownloading
1170 || item.IsDownloadPending
1171 || (item.InstallTime.TryGetValue(out var workshopInstallTime)
1172 && pkg.InstallTime.TryUnwrap(out var localInstallTime)
1173 && localInstallTime.ToUtcValue() < workshopInstallTime)));
1175 modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
1178 private static bool CanHostServer()
1179 => EosInterface.IdQueries.IsLoggedIntoEosConnect
1180 || SteamManager.IsInitialized
1181 || AssemblyInfo.CurrentConfiguration == AssemblyInfo.Configuration.Debug;
1183 public override void Update(
double deltaTime)
1185 hostServerButton.Enabled = CanHostServer();
1187 gameAnalyticsStatusText.Text = TextManager.Get($
"GameAnalyticsStatus.{GameAnalyticsManager.UserConsented}");
1189 UpdateOutOfDateWorkshopItemCount();
1190 modUpdatesButton.Visible = modUpdateStatus.Count > 0;
1192 if (modUpdatesButton.Visible)
1194 var modButtonLabelSize =
1195 modsButton.Font.MeasureString(modsButton.Text).ToPoint()
1196 +
new Point(GUI.IntScale(25));
1197 modUpdatesButton.RectTransform.AbsoluteOffset =
1198 (modButtonLabelSize.X, modsButton.Rect.Height / 2 - modUpdatesButton.Rect.Height / 2);
1201 switch (selectedTab)
1204 campaignSetupUI.
Update();
1211 graphics.Clear(Color.Black);
1213 if (backgroundSprite ==
null)
1216 backgroundSprite =
new Sprite(
"Content/UnstableBackground.png", sourceRectangle:
null);
1218 if (GUIStyle.GetComponentStyle(
"MainMenuBackground") is { } mainMenuStyle &&
1221 backgroundSprite = sprites.GetRandomUnsynced()?.
Sprite;
1226 var vignette = GUIStyle.GetComponentStyle(
"mainmenuvignette")?.GetDefaultSprite();
1230 (
int)(vignette.size.X * vignetteScale / 2), 0,
1233 if (backgroundSprite?.Texture !=
null)
1235 GUI.DrawBackgroundSprite(spriteBatch, backgroundSprite, Color.White, drawArea);
1238 if (vignette !=
null)
1240 vignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f, vignetteScale);
1244 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
1248 DrawBackground(graphics, spriteBatch);
1250 GUI.Draw(Cam, spriteBatch);
1255 private void StartGame(
SubmarineInfo selectedSub,
string savePath,
string mapSeed, CampaignSettings settings)
1257 if (
string.IsNullOrEmpty(savePath)) {
return; }
1259 var existingSaveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
1260 if (existingSaveFiles.Any(s => s.FilePath == savePath))
1262 new GUIMessageBox(TextManager.Get(
"SaveNameInUseHeader"), TextManager.Get(
"SaveNameInUseText"));
1266 if (selectedSub ==
null)
1268 new GUIMessageBox(TextManager.Get(
"SubNotSelected"), TextManager.Get(
"SelectSubRequest"));
1272 if (!Directory.Exists(SaveUtil.TempPath))
1274 Directory.CreateDirectory(SaveUtil.TempPath);
1279 File.Copy(selectedSub.
FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.
Name +
".sub"),
true);
1281 catch (System.IO.IOException e)
1283 DebugConsole.ThrowError(
"Copying the file \"" + selectedSub.
FilePath +
"\" failed. The file may have been deleted or in use by another process. Try again or select another submarine.", e);
1284 GameAnalyticsManager.AddErrorEventOnce(
1285 "MainMenuScreen.StartGame:IOException" + selectedSub.
Name,
1286 GameAnalyticsManager.ErrorSeverity.Error,
1287 "Copying a submarine file failed. " + e.Message +
"\n" + Environment.StackTrace.CleanupStackTrace());
1291 GameMain.LuaCs.CheckInitialize();
1293 selectedSub =
new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.
Name +
".sub"));
1295 GameMain.GameSession =
new GameSession(selectedSub, savePath, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
1296 GameMain.GameSession.CrewManager.ClearCharacterInfos();
1297 foreach (var characterInfo
in campaignSetupUI.
CharacterMenus.Select(m => m.CharacterInfo))
1299 GameMain.GameSession.CrewManager.AddCharacterInfo(characterInfo);
1301 ((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel();
1304 private void LoadGame(
string saveFile)
1306 if (
string.IsNullOrWhiteSpace(saveFile))
return;
1308 GameMain.LuaCs.CheckInitialize();
1312 SaveUtil.LoadGame(saveFile);
1316 DebugConsole.ThrowError(
"Loading save \"" + saveFile +
"\" failed", e);
1325 private void CreateCampaignSetupUI()
1327 menuTabs[Tab.NewGame].ClearChildren();
1328 menuTabs[Tab.LoadGame].ClearChildren();
1330 var innerNewGame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.9f), menuTabs[Tab.NewGame].RectTransform,
Anchor.Center))
1333 RelativeSpacing = 0.02f
1335 var newGameContent =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.95f), innerNewGame.RectTransform,
Anchor.Center),
1336 style:
"InnerFrame");
1338 var paddedLoadGame =
new GUIFrame(
new RectTransform(
new Vector2(0.9f, 0.9f), menuTabs[Tab.LoadGame].RectTransform,
Anchor.Center) { AbsoluteOffset = new Point(0, 10) },
1341 campaignSetupUI =
new SinglePlayerCampaignSetupUI(newGameContent, paddedLoadGame)
1343 LoadGame = LoadGame,
1344 StartNewGame = StartGame
1348 private void CreateHostServerFields()
1350 menuTabs[Tab.HostServer].ClearChildren();
1354 var name = serverSettings.GetAttributeString(
"name",
"");
1355 var password = serverSettings.GetAttributeString(
"password",
"");
1356 var isPublic = serverSettings.GetAttributeBool(
"IsPublic",
true);
1357 var banAfterWrongPassword = serverSettings.GetAttributeBool(
"banafterwrongpassword",
false);
1359 int maxPlayersElement = serverSettings.GetAttributeInt(
"maxplayers", 8);
1360 if (maxPlayersElement > NetConfig.MaxPlayers)
1362 DebugConsole.AddWarning($
"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.");
1364 int maxPlayers = Math.Clamp(maxPlayersElement, min: 1, max: NetConfig.MaxPlayers);
1366 var karmaEnabled = serverSettings.GetAttributeBool(
"karmaenabled",
true);
1367 var selectedPlayStyle = serverSettings.GetAttributeEnum(
"playstyle",
PlayStyle.Casual);
1369 Vector2 textLabelSize =
new Vector2(1.0f, 0.05f);
1370 Alignment textAlignment = Alignment.CenterLeft;
1371 Vector2 textFieldSize =
new Vector2(0.5f, 1.0f);
1372 Vector2 tickBoxSize =
new Vector2(0.4f, 0.04f);
1373 var content =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.7f, 0.95f), menuTabs[Tab.HostServer].RectTransform,
Anchor.Center), childAnchor:
Anchor.TopCenter)
1375 RelativeSpacing = 0.01f,
1378 GUIComponent parent = content;
1380 var header =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"HostServerButton"), textAlignment: Alignment.Center, font: GUIStyle.LargeFont) {
ForceUpperCase =
ForceUpperCase.Yes };
1381 header.RectTransform.IsFixedSize =
true;
1385 var playstyleContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.35f, 0.1f), parent.RectTransform), style:
null, color: Color.Black);
1387 playstyleBanner =
new GUIImage(
new RectTransform(
new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform),
1388 GUIStyle.GetComponentStyle($
"PlayStyleBanner.{PlayStyle.Serious}").GetSprite(GUIComponent.ComponentState.None), scaleToFit:
true)
1395 new GUIFrame(
new RectTransform(playstyleBanner.
Rect.Size +
new Point(1), playstyleBanner.
RectTransform,
Anchor.Center),
"InnerGlow", color: Color.Black);
1397 new GUITextBlock(
new RectTransform(
new Vector2(0.15f, 0.05f), playstyleBanner.
RectTransform) { RelativeOffset = new Vector2(0.01f, 0.03f) },
1398 "playstyle name goes here", font: GUIStyle.SmallFont, textAlignment: Alignment.Center, textColor: Color.White, style:
"GUISlopedHeader");
1400 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform,
Anchor.CenterLeft)
1401 { RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
1402 style:
"UIToggleButton")
1404 OnClicked = (btn, userdata) =>
1406 int playStyleIndex = (int)playstyleBanner.
UserData - 1;
1407 if (playStyleIndex < 0) { playStyleIndex = Enum.GetValues(typeof(
PlayStyle)).Length - 1; }
1408 SetServerPlayStyle((
PlayStyle)playStyleIndex);
1411 }.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally);
1413 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform,
Anchor.CenterRight)
1414 { RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
1415 style:
"UIToggleButton")
1417 OnClicked = (btn, userdata) =>
1419 int playStyleIndex = (int)playstyleBanner.
UserData + 1;
1420 if (playStyleIndex >= Enum.GetValues(typeof(
PlayStyle)).Length) { playStyleIndex = 0; }
1421 SetServerPlayStyle((
PlayStyle)playStyleIndex);
1426 LocalizedString longestPlayStyleStr =
"";
1429 LocalizedString playStyleStr = TextManager.Get(
"servertagdescription." + playStyle);
1430 if (playStyleStr.Length > longestPlayStyleStr.Length) { longestPlayStyleStr = playStyleStr; }
1433 playstyleDescription =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), playstyleContainer.RectTransform,
Anchor.BottomCenter),
1434 longestPlayStyleStr, style:
null, wrap:
true)
1436 Color = Color.Black * 0.8f,
1437 TextColor = GUIStyle.GetComponentStyle(
"GUITextBlock").
TextColor
1439 playstyleDescription.
Padding = Vector4.One * 10.0f * GUI.Scale;
1443 playstyleContainer.RectTransform.NonScaledSize =
new Point(playstyleContainer.Rect.Width, playstyleBanner.
Rect.Height + playstyleDescription.
Rect.Height);
1444 playstyleContainer.RectTransform.IsFixedSize =
true;
1446 SetServerPlayStyle(selectedPlayStyle);
1451 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.025f), content.RectTransform), style:
null);
1453 var label =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"ServerName"), textAlignment: textAlignment);
1454 serverNameBox =
new GUITextBox(
new RectTransform(textFieldSize, label.RectTransform,
Anchor.CenterRight), text: name, textAlignment: textAlignment)
1456 MaxTextLength = NetConfig.ServerNameMaxLength,
1461 var maxPlayersLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"MaxPlayers"), textAlignment: textAlignment);
1462 var buttonContainer =
new GUILayoutGroup(
new RectTransform(textFieldSize, maxPlayersLabel.RectTransform,
Anchor.CenterRight), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
1465 RelativeSpacing = 0.1f
1467 new GUIButton(
new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"GUIMinusButton", textAlignment: Alignment.Center)
1470 OnClicked = ChangeMaxPlayers,
1473 maxPlayersBox =
new GUITextBox(
new RectTransform(
new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center)
1475 Text = maxPlayers.ToString()
1477 maxPlayersBox.
OnEnterPressed += (GUITextBox sender,
string text) =>
1482 maxPlayersBox.
OnDeselected += (GUITextBox sender, Microsoft.Xna.Framework.Input.Keys key) =>
1484 int.TryParse(maxPlayersBox.
Text, out
int currMaxPlayers);
1485 currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers, 1, NetConfig.MaxPlayers);
1486 maxPlayersBox.
Text = currMaxPlayers.ToString();
1488 new GUIButton(
new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"GUIPlusButton", textAlignment: Alignment.Center)
1491 OnClicked = ChangeMaxPlayers,
1494 maxPlayersLabel.RectTransform.IsFixedSize =
true;
1496 label =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"Password"), textAlignment: textAlignment);
1497 passwordBox =
new GUITextBox(
new RectTransform(textFieldSize, label.RectTransform,
Anchor.CenterRight), text: password, textAlignment: textAlignment)
1503 var languageLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform),
1504 TextManager.Get(
"Language"), textAlignment: textAlignment);
1505 languageDropdown =
new GUIDropDown(
new RectTransform(textFieldSize, languageLabel.RectTransform,
Anchor.CenterRight));
1506 foreach (var language
in ServerLanguageOptions.Options)
1508 languageDropdown.
AddItem(language.Label, language.Identifier);
1510 var defaultLanguage = ServerLanguageOptions.PickLanguage(GameSettings.CurrentConfig.Language);
1511 var settingsLanguage = serverSettings.GetAttributeIdentifier(
"language", defaultLanguage.Value).ToLanguageIdentifier();
1512 if (!ServerLanguageOptions.Options.Any(o => o.Identifier == settingsLanguage))
1514 settingsLanguage = defaultLanguage;
1516 languageDropdown.
Select(ServerLanguageOptions.Options.FindIndex(o => o.Identifier == settingsLanguage));
1518 var serverExecutableLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform),
1519 TextManager.Get(
"ServerExecutable"), textAlignment: textAlignment);
1520 const string vanillaServerOption =
"Vanilla";
1521 serverExecutableDropdown
1522 =
new GUIDropDown(
new RectTransform(textFieldSize, serverExecutableLabel.RectTransform,
Anchor.CenterRight),
1523 vanillaServerOption);
1526 serverExecutableDropdown.
AddItem(vanillaServerOption, userData:
null);
1527 serverExecutableDropdown.
OnSelected = (selected, userData) =>
1529 if (userData !=
null)
1531 var warningBox =
new GUIMessageBox(headerText: TextManager.Get(
"Warning"),
1532 text: TextManager.GetWithVariable(
"ModServerExesAtYourOwnRisk",
"[exename]", serverExecutableDropdown.
Text),
1533 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
1534 warningBox.Buttons[0].OnClicked = (_, __) =>
1539 warningBox.Buttons[1].OnClicked = (_, __) =>
1541 serverExecutableDropdown.
Select(0);
1547 serverExecutableDropdown.
Text = ToolBox.LimitString(serverExecutableDropdown.
Text,
1548 serverExecutableDropdown.
Font, serverExecutableDropdown.
Rect.Width * 8 / 10);
1552 serverExecutableLabel.RectTransform.IsFixedSize =
true;
1556 var tickboxAreaUpper =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal:
true);
1558 isPublicBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get(
"PublicServer"))
1561 ToolTip = TextManager.Get(
"PublicServerToolTip")
1564 wrongPasswordBanBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get(
"ServerSettingsBanAfterWrongPassword"))
1569 tickboxAreaUpper.RectTransform.IsFixedSize =
true;
1573 var tickboxAreaLower =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal:
true);
1575 karmaBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get(
"HostServerKarmaSetting"))
1578 ToolTip = TextManager.Get(
"hostserverkarmasettingtooltip")
1581 tickboxAreaLower.RectTransform.IsFixedSize =
true;
1584 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.05f), content.RectTransform), style:
null);
1586 new GUIButton(
new RectTransform(
new Vector2(0.4f, 0.07f), content.RectTransform), TextManager.Get(
"StartServerButton"), style:
"GUIButtonLarge")
1588 OnClicked = (btn, userdata) =>
1595 void CheckServerName()
1597 string name = serverNameBox.
Text;
1598 if (
string.IsNullOrEmpty(name))
1600 serverNameBox.
Flash();
1603 if (isPublicBox.
Selected && ForbiddenWordFilter.IsForbidden(name, out
string forbiddenWord))
1605 var msgBox =
new GUIMessageBox(
"",
1606 TextManager.GetWithVariables(
"forbiddenservernameverification", (
"[forbiddenword]", forbiddenWord), (
"[servername]", name)),
1607 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1608 msgBox.Buttons[0].OnClicked += (_, __) =>
1614 msgBox.Buttons[1].OnClicked += msgBox.Close;
1620 void CheckServerExe()
1622 if (serverExecutableDropdown?.SelectedData is ServerExecutableFile serverExe &&
1623 serverExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
1625 var msgBox =
new GUIMessageBox(
string.Empty,
1626 TextManager.GetWithVariables(
"versionmismatchwarning",
1627 (
"[gameversion]", serverExe.ContentPackage.GameVersion.ToString()),
1628 (
"[contentversion]", GameMain.VanillaContent.GameVersion.ToString())) +
"\n\n"+
1629 TextManager.GetWithVariable(
"versionmismatch.verifylaunch",
"[exename]", serverExe.ContentPackage.Name),
1630 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1631 msgBox.Buttons[0].OnClicked += (_, __) =>
1637 msgBox.Buttons[1].OnClicked += msgBox.Close;
1644 private void SetServerPlayStyle(
PlayStyle playStyle)
1646 playstyleBanner.
Sprite = GUIStyle
1647 .GetComponentStyle($
"PlayStyleBanner.{playStyle}")
1648 .GetSprite(GUIComponent.ComponentState.None);
1649 playstyleBanner.
UserData = playStyle;
1651 var nameText = playstyleBanner.
GetChild<GUITextBlock>();
1652 nameText.Text = TextManager.AddPunctuation(
':', TextManager.Get(
"serverplaystyle"), TextManager.Get(
"servertag." + playStyle));
1655 nameText.RectTransform.NonScaledSize = (nameText.Font.MeasureString(nameText.Text) +
new Vector2(25, 10) * GUI.Scale).ToPoint();
1657 playstyleDescription.
Text = TextManager.Get(
"servertagdescription." + playStyle);
1659 Alignment.CenterLeft : Alignment.Center;
1663 private void FetchRemoteContent()
1665 string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
1666 if (
string.IsNullOrEmpty(remoteContentUrl)) {
return; }
1669 var client =
new RestClient(remoteContentUrl);
1670 var request =
new RestRequest(
"MenuContent.xml", Method.GET);
1671 TaskPool.Add(
"RequestMainMenuRemoteContent", client.ExecuteAsync(request),
1672 RemoteContentReceived);
1678 DebugConsole.ThrowError(
"Fetching remote content to the main menu failed.", e);
1680 GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.FetchRemoteContent:Exception", GameAnalyticsManager.ErrorSeverity.Error,
1681 "Fetching remote content to the main menu failed. " + e.Message);
1686 private void RemoteContentReceived(Task t)
1690 if (!t.TryGetResult(out IRestResponse remoteContentResponse)) {
throw new Exception(
"Task did not return a valid result"); }
1691 if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
1693 DebugConsole.AddWarning(
1694 "Failed to receive remote main menu content. " +
1695 "There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
1696 $
"(error code: {remoteContentResponse.StatusCode})");
1699 string xml = remoteContentResponse.Content;
1700 int index = xml.IndexOf(
'<');
1701 if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
1702 if (!
string.IsNullOrWhiteSpace(xml))
1704 remoteContentDoc = XDocument.Parse(xml);
1705 foreach (var subElement
in remoteContentDoc?.Root.Elements())
1707 GUIComponent.FromXML(subElement.FromPackage(
null), remoteContentContainer.RectTransform);
1715 DebugConsole.ThrowError(
"Reading received remote main menu content failed.", e);
1717 GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.RemoteContentReceived:Exception", GameAnalyticsManager.ErrorSeverity.Error,
1718 "Reading received remote main menu content failed. " + e.Message);
Stores information about the Character that is needed between rounds in the menu etc....
static readonly Identifier HumanSpeciesName
static CompletedTutorials Instance
bool Contains(Identifier identifier)
Color GetAttributeColor(string key, in Color def)
GUIComponent GetChild(int index)
virtual RichString ToolTip
RectTransform RectTransform
IEnumerable< GUIComponent > Children
GUIComponent that can be used to render custom content on the UI
GUIComponent AddItem(LocalizedString text, object userData=null, LocalizedString toolTip=null, Color? color=null, Color? textColor=null)
OnSelectedHandler OnSelected
GUIComponent SelectedComponent
override void RemoveChild(GUIComponent child)
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
void Select(object userData, Force force=Force.No, AutoScroll autoScroll=AutoScroll.Enabled)
void CalculateHeightFromText(int padding=0, bool removeExtraSpacing=false)
LocalizedString WrappedText
TextBoxEvent OnDeselected
OnEnterHandler OnEnterPressed
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectOffset=null)
static RasterizerState ScissorTestEnable
static SubEditorScreen SubEditorScreen
static int GraphicsHeight
static readonly Version Version
static GameScreen GameScreen
static MainMenuScreen MainMenuScreen
static GameModePreset DevSandbox
static JobPrefab Get(Identifier identifier)
bool Contains(string subStr, StringComparison comparison=StringComparison.Ordinal)
static readonly PrefabCollection< LocationType > Prefabs
void QuickStart(bool fixedSeed=false, Identifier sub=default, float difficulty=50, LevelGenerationParams levelGenerationParams=null)
override void Update(double deltaTime)
bool ReturnToMainMenu(GUIButton button, object obj)
void DrawBackground(GraphicsDevice graphics, SpriteBatch spriteBatch)
override void AddToGUIUpdateList()
By default, submits the screen's main GUIFrame and, if requested upon construction,...
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
void ResetModUpdateButton()
static readonly Queue< ulong > WorkshopItemsToUpdate
static void UpdateInstanceTutorialButtons()
MainMenuScreen(GameMain game)
const string SettingsFile
override void CreateLoadMenu(IEnumerable< CampaignMode.SaveInfo > saveFiles=null)
CharacterInfo.AppearanceCustomizationMenu[] CharacterMenus
void CreateDefaultSaveName()
void UpdateSubList(IEnumerable< SubmarineInfo > submarines)
void SetPage(int pageIndex)
void EnsureLazyLoaded(bool isAsync=false)
Sprite(ContentXElement element, string path="", string file="", bool lazyLoad=false, float sourceRectScale=1)
ContentXElement SourceElement
Reference to the xml element from where the sprite was created. Can be null if the sprite was not def...
void ClearBackedUpSubInfo()
static IEnumerable< SubmarineInfo > SavedSubmarines
LocalizedString DisplayName
readonly TutorialPrefab TutorialPrefab
LocalizedString Description