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()));
931 missionPrefabs:
null);
933 gamesession.StartRound(fixedSeed ?
"abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams);
936 Identifier[] jobIdentifiers =
new Identifier[] {
937 "captain".ToIdentifier(),
938 "engineer".ToIdentifier(),
939 "mechanic".ToIdentifier(),
940 "securityofficer".ToIdentifier(),
941 "medicaldoctor".ToIdentifier() };
942 foreach (Identifier job
in jobIdentifiers)
945 var variant = Rand.Range(0, jobPrefab.Variants);
947 if (characterInfo.Job ==
null)
949 DebugConsole.ThrowError(
"Failed to find the job \"" + job +
"\"!");
951 gamesession.CrewManager.AddCharacterInfo(characterInfo);
953 gamesession.CrewManager.InitSinglePlayerRound();
956 private void ShowTutorialSkipWarning(Tab tabToContinueTo)
958 var tutorialSkipWarning =
new GUIMessageBox(
"", TextManager.Get(
"tutorialskipwarning"),
new LocalizedString[] { TextManager.Get(
"tutorialwarningskiptutorials"), TextManager.Get(
"tutorialwarningplaytutorials") });
961 => (btn, userdata) =>
963 var config = GameSettings.CurrentConfig;
964 config.TutorialSkipWarning =
false;
965 GameSettings.SetCurrentConfig(config);
966 GameSettings.SaveCurrentConfig();
967 tutorialSkipWarning.Close();
972 tutorialSkipWarning.Buttons[0].OnClicked += proceedToTab(tabToContinueTo);
973 tutorialSkipWarning.Buttons[1].OnClicked += proceedToTab(Tab.Tutorials);
978 base.AddToGUIUpdateList();
982 campaignSetupUI.
CharacterMenus?.ForEach(
static m => m.AddToGUIUpdateList());
987 private void UpdateTutorialList()
999 private bool ChangeMaxPlayers(GUIButton button,
object obj)
1001 int.TryParse(maxPlayersBox.
Text, out
int currMaxPlayers);
1002 currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers + (
int)button.UserData, 1, NetConfig.MaxPlayers);
1003 maxPlayersBox.
Text = currMaxPlayers.ToString();
1007 private void TryStartServer()
1009 if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
1011 var waitBox =
new GUIMessageBox(TextManager.Get(
"pleasewait"), TextManager.Get(
"waitforsubmarinehashcalculations"),
new LocalizedString[] { TextManager.Get(
"cancel") });
1012 var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox),
"WaitForSubmarineHashCalculations");
1013 waitBox.Buttons[0].OnClicked += (btn, userdata) =>
1015 CoroutineManager.StopCoroutines(waitCoroutine);
1025 private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
1027 LocalizedString originalText = messageBox.Text.Text;
1031 doneCount = SubmarineInfo.SavedSubmarines.Count(s => !s.CalculatingHash);
1032 messageBox.Text.Text = originalText + $
" ({doneCount}/{SubmarineInfo.SavedSubmarines.Count()})";
1033 yield
return CoroutineStatus.Running;
1034 }
while (doneCount < SubmarineInfo.SavedSubmarines.Count());
1037 yield
return CoroutineStatus.Success;
1040 private void StartServer()
1042 string name = serverNameBox.
Text;
1044 GameMain.ResetNetLobbyScreen();
1049 f.ContentPackage != GameMain.VanillaContent)
1051 fileName = Path.Combine(
1052 Path.GetDirectoryName(f.Path.Value),
1053 Path.GetFileNameWithoutExtension(f.Path.Value));
1061 fileName =
"DedicatedServer.exe";
1063 fileName =
"./DedicatedServer";
1067 var arguments =
new List<string>
1070 "-public", isPublicBox.
Selected.ToString(),
1072 "-banafterwrongpassword", wrongPasswordBanBox.
Selected.ToString(),
1073 "-karmaenabled", (!karmaBox.
Selected).ToString(),
1074 "-maxplayers", maxPlayersBox.
Text,
1078 if (!
string.IsNullOrWhiteSpace(passwordBox.
Text))
1080 arguments.Add(
"-password");
1081 arguments.Add(passwordBox.
Text);
1085 arguments.Add(
"-nopassword");
1088 var puids = EosInterface.IdQueries.GetLoggedInPuids();
1090 var endpoints =
new List<Endpoint>();
1091 if (SteamManager.GetSteamId().TryUnwrap(out var steamId))
1095 if (puids.Length > 0)
1099 if (endpoints.Count == 0)
1101 endpoints.Add(
new LidgrenEndpoint(IPAddress.Loopback, NetConfig.DefaultPort));
1104 if (endpoints.First() is
P2PEndpoint firstEndpoint)
1106 arguments.Add(
"-endpoint");
1107 arguments.Add(firstEndpoint.StringRepresentation);
1109 int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
1110 arguments.Add(
"-ownerkey");
1111 arguments.Add(ownerKey.ToString());
1113 var processInfo =
new ProcessStartInfo
1115 FileName = fileName,
1116 WorkingDirectory = Directory.GetCurrentDirectory(),
1118 CreateNoWindow =
true,
1119 UseShellExecute =
false,
1120 WindowStyle = ProcessWindowStyle.Hidden
1123 arguments.ForEach(processInfo.ArgumentList.Add);
1124 ChildServerRelay.Start(processInfo);
1127 GameMain.Client =
new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(
1128 SteamManager.GetUsername().FallbackNullOrEmpty(name)),
1129 endpoints.ToImmutableArray(),
1131 Option.Some(ownerKey));
1135 DebugConsole.ThrowError(
"Failed to start server", e);
1139 private bool QuitClicked(GUIButton button,
object obj)
1145 private void UpdateOutOfDateWorkshopItemCount()
1147 if (DateTime.Now < modUpdateStatus.WhenToRefresh) {
return; }
1148 if (!SteamManager.IsInitialized) {
return; }
1150 var installedPackages = ContentPackageManager.WorkshopPackages;
1152 var ids = SteamManager.Workshop.GetSubscribedItemIds()
1153 .Select(
id =>
id.Value)
1154 .Union(installedPackages
1155 .
Select(pkg => pkg.UgcId)
1157 .OfType<SteamWorkshopId>()
1158 .Select(
id =>
id.Value));
1162 .Select(
id =>
new Steamworks.Ugc.Item(
id))
1164 installedPackages.FirstOrDefault(p
1165 => p.UgcId.TryUnwrap(out SteamWorkshopId
id) &&
id.Value == item.Id)
1170 && (item.IsDownloading
1171 || item.IsDownloadPending
1172 || (item.InstallTime.TryGetValue(out var workshopInstallTime)
1173 && pkg.InstallTime.TryUnwrap(out var localInstallTime)
1174 && localInstallTime.ToUtcValue() < workshopInstallTime)));
1176 modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
1179 private static bool CanHostServer()
1180 => EosInterface.IdQueries.IsLoggedIntoEosConnect
1181 || SteamManager.IsInitialized
1182 || AssemblyInfo.CurrentConfiguration == AssemblyInfo.Configuration.Debug;
1184 public override void Update(
double deltaTime)
1186 hostServerButton.Enabled = CanHostServer();
1188 gameAnalyticsStatusText.Text = TextManager.Get($
"GameAnalyticsStatus.{GameAnalyticsManager.UserConsented}");
1190 UpdateOutOfDateWorkshopItemCount();
1191 modUpdatesButton.Visible = modUpdateStatus.Count > 0;
1193 if (modUpdatesButton.Visible)
1195 var modButtonLabelSize =
1196 modsButton.Font.MeasureString(modsButton.Text).ToPoint()
1197 +
new Point(GUI.IntScale(25));
1198 modUpdatesButton.RectTransform.AbsoluteOffset =
1199 (modButtonLabelSize.X, modsButton.Rect.Height / 2 - modUpdatesButton.Rect.Height / 2);
1202 switch (selectedTab)
1205 campaignSetupUI.
Update();
1212 graphics.Clear(Color.Black);
1214 if (backgroundSprite ==
null)
1217 backgroundSprite =
new Sprite(
"Content/UnstableBackground.png", sourceRectangle:
null);
1219 if (GUIStyle.GetComponentStyle(
"MainMenuBackground") is { } mainMenuStyle &&
1222 backgroundSprite = sprites.GetRandomUnsynced()?.
Sprite;
1227 var vignette = GUIStyle.GetComponentStyle(
"mainmenuvignette")?.GetDefaultSprite();
1231 (
int)(vignette.size.X * vignetteScale / 2), 0,
1234 if (backgroundSprite?.Texture !=
null)
1236 GUI.DrawBackgroundSprite(spriteBatch, backgroundSprite, Color.White, drawArea);
1239 if (vignette !=
null)
1241 vignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f, vignetteScale);
1245 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
1249 DrawBackground(graphics, spriteBatch);
1251 GUI.Draw(Cam, spriteBatch);
1256 private void StartGame(
SubmarineInfo selectedSub,
string savePath,
string mapSeed, CampaignSettings settings)
1258 if (
string.IsNullOrEmpty(savePath)) {
return; }
1260 var existingSaveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
1261 if (existingSaveFiles.Any(s => s.FilePath == savePath))
1263 new GUIMessageBox(TextManager.Get(
"SaveNameInUseHeader"), TextManager.Get(
"SaveNameInUseText"));
1267 if (selectedSub ==
null)
1269 new GUIMessageBox(TextManager.Get(
"SubNotSelected"), TextManager.Get(
"SelectSubRequest"));
1273 if (!Directory.Exists(SaveUtil.TempPath))
1275 Directory.CreateDirectory(SaveUtil.TempPath, catchUnauthorizedAccessExceptions:
true);
1280 File.Copy(selectedSub.
FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.
Name +
".sub"), overwrite:
true, catchUnauthorizedAccessExceptions:
false);
1282 catch (System.IO.IOException e)
1284 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);
1285 GameAnalyticsManager.AddErrorEventOnce(
1286 "MainMenuScreen.StartGame:IOException" + selectedSub.
Name,
1287 GameAnalyticsManager.ErrorSeverity.Error,
1288 "Copying a submarine file failed. " + e.Message +
"\n" + Environment.StackTrace.CleanupStackTrace());
1292 GameMain.LuaCs.CheckInitialize();
1294 selectedSub =
new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.
Name +
".sub"));
1296 GameMain.GameSession =
new GameSession(selectedSub, Option.None, CampaignDataPath.CreateRegular(savePath), GameModePreset.SinglePlayerCampaign, settings, mapSeed);
1297 GameMain.GameSession.CrewManager.ClearCharacterInfos();
1298 foreach (var characterInfo
in campaignSetupUI.
CharacterMenus.Select(m => m.CharacterInfo))
1300 GameMain.GameSession.CrewManager.AddCharacterInfo(characterInfo);
1302 ((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel();
1305 private void LoadGame(
string path, Option<uint> backupIndex)
1307 if (
string.IsNullOrWhiteSpace(path))
return;
1309 GameMain.LuaCs.CheckInitialize();
1313 CampaignDataPath dataPath =
1314 backupIndex.TryUnwrap(out uint index)
1315 ?
new CampaignDataPath(loadPath: SaveUtil.GetBackupPath(path, index), path)
1316 : CampaignDataPath.CreateRegular(path);
1318 SaveUtil.LoadGame(dataPath);
1322 DebugConsole.ThrowError(
"Loading save \"" + path +
"\" failed", e);
1331 private void CreateCampaignSetupUI()
1333 menuTabs[Tab.NewGame].ClearChildren();
1334 menuTabs[Tab.LoadGame].ClearChildren();
1336 var innerNewGame =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.9f), menuTabs[Tab.NewGame].RectTransform,
Anchor.Center))
1339 RelativeSpacing = 0.02f
1341 var newGameContent =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.95f), innerNewGame.RectTransform,
Anchor.Center),
1342 style:
"InnerFrame");
1344 var paddedLoadGame =
new GUIFrame(
new RectTransform(
new Vector2(0.9f, 0.9f), menuTabs[Tab.LoadGame].RectTransform,
Anchor.Center) { AbsoluteOffset = new Point(0, 10) },
1347 campaignSetupUI =
new SinglePlayerCampaignSetupUI(newGameContent, paddedLoadGame)
1349 LoadGame = LoadGame,
1350 StartNewGame = StartGame
1354 private void CreateHostServerFields()
1356 menuTabs[Tab.HostServer].ClearChildren();
1360 var name = serverSettings.GetAttributeString(
"name",
"");
1361 var password = serverSettings.GetAttributeString(
"password",
"");
1362 var isPublic = serverSettings.GetAttributeBool(
"IsPublic",
true);
1363 var banAfterWrongPassword = serverSettings.GetAttributeBool(
"banafterwrongpassword",
false);
1365 int maxPlayersElement = serverSettings.GetAttributeInt(
"maxplayers", 8);
1366 if (maxPlayersElement > NetConfig.MaxPlayers)
1368 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.");
1370 int maxPlayers = Math.Clamp(maxPlayersElement, min: 1, max: NetConfig.MaxPlayers);
1372 var karmaEnabled = serverSettings.GetAttributeBool(
"karmaenabled",
true);
1373 var selectedPlayStyle = serverSettings.GetAttributeEnum(
"playstyle",
PlayStyle.Casual);
1375 Vector2 textLabelSize =
new Vector2(1.0f, 0.05f);
1376 Alignment textAlignment = Alignment.CenterLeft;
1377 Vector2 textFieldSize =
new Vector2(0.5f, 1.0f);
1378 Vector2 tickBoxSize =
new Vector2(0.4f, 0.04f);
1379 var content =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.7f, 0.95f), menuTabs[Tab.HostServer].RectTransform,
Anchor.Center), childAnchor:
Anchor.TopCenter)
1381 RelativeSpacing = 0.01f,
1384 GUIComponent parent = content;
1386 var header =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"HostServerButton"), textAlignment: Alignment.Center, font: GUIStyle.LargeFont) {
ForceUpperCase =
ForceUpperCase.Yes };
1387 header.RectTransform.IsFixedSize =
true;
1391 var playstyleContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.35f, 0.1f), parent.RectTransform), style:
null, color: Color.Black);
1393 playstyleBanner =
new GUIImage(
new RectTransform(
new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform),
1394 GUIStyle.GetComponentStyle($
"PlayStyleBanner.{PlayStyle.Serious}").GetSprite(GUIComponent.ComponentState.None), scaleToFit:
true)
1401 new GUIFrame(
new RectTransform(playstyleBanner.
Rect.Size +
new Point(1), playstyleBanner.
RectTransform,
Anchor.Center),
"InnerGlow", color: Color.Black);
1403 new GUITextBlock(
new RectTransform(
new Vector2(0.15f, 0.05f), playstyleBanner.
RectTransform) { RelativeOffset = new Vector2(0.01f, 0.03f) },
1404 "playstyle name goes here", font: GUIStyle.SmallFont, textAlignment: Alignment.Center, textColor: Color.White, style:
"GUISlopedHeader");
1406 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform,
Anchor.CenterLeft)
1407 { RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
1408 style:
"UIToggleButton")
1410 OnClicked = (btn, userdata) =>
1412 int playStyleIndex = (int)playstyleBanner.
UserData - 1;
1413 if (playStyleIndex < 0) { playStyleIndex = Enum.GetValues(typeof(
PlayStyle)).Length - 1; }
1414 SetServerPlayStyle((
PlayStyle)playStyleIndex);
1417 }.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally);
1419 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform,
Anchor.CenterRight)
1420 { RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
1421 style:
"UIToggleButton")
1423 OnClicked = (btn, userdata) =>
1425 int playStyleIndex = (int)playstyleBanner.
UserData + 1;
1426 if (playStyleIndex >= Enum.GetValues(typeof(
PlayStyle)).Length) { playStyleIndex = 0; }
1427 SetServerPlayStyle((
PlayStyle)playStyleIndex);
1432 LocalizedString longestPlayStyleStr =
"";
1435 LocalizedString playStyleStr = TextManager.Get(
"servertagdescription." + playStyle);
1436 if (playStyleStr.Length > longestPlayStyleStr.Length) { longestPlayStyleStr = playStyleStr; }
1439 playstyleDescription =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), playstyleContainer.RectTransform,
Anchor.BottomCenter),
1440 longestPlayStyleStr, style:
null, wrap:
true)
1442 Color = Color.Black * 0.8f,
1443 TextColor = GUIStyle.GetComponentStyle(
"GUITextBlock").
TextColor
1445 playstyleDescription.
Padding = Vector4.One * 10.0f * GUI.Scale;
1449 playstyleContainer.RectTransform.NonScaledSize =
new Point(playstyleContainer.Rect.Width, playstyleBanner.
Rect.Height + playstyleDescription.
Rect.Height);
1450 playstyleContainer.RectTransform.IsFixedSize =
true;
1452 SetServerPlayStyle(selectedPlayStyle);
1457 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.025f), content.RectTransform), style:
null);
1459 var label =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"ServerName"), textAlignment: textAlignment);
1460 serverNameBox =
new GUITextBox(
new RectTransform(textFieldSize, label.RectTransform,
Anchor.CenterRight), text: name, textAlignment: textAlignment)
1462 MaxTextLength = NetConfig.ServerNameMaxLength,
1467 var maxPlayersLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"MaxPlayers"), textAlignment: textAlignment);
1468 var buttonContainer =
new GUILayoutGroup(
new RectTransform(textFieldSize, maxPlayersLabel.RectTransform,
Anchor.CenterRight), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
1471 RelativeSpacing = 0.1f
1473 new GUIButton(
new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"GUIMinusButton", textAlignment: Alignment.Center)
1476 OnClicked = ChangeMaxPlayers,
1479 maxPlayersBox =
new GUITextBox(
new RectTransform(
new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center)
1481 Text = maxPlayers.ToString()
1483 maxPlayersBox.
OnEnterPressed += (GUITextBox sender,
string text) =>
1488 maxPlayersBox.
OnDeselected += (GUITextBox sender, Microsoft.Xna.Framework.Input.Keys key) =>
1490 int.TryParse(maxPlayersBox.
Text, out
int currMaxPlayers);
1491 currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers, 1, NetConfig.MaxPlayers);
1492 maxPlayersBox.
Text = currMaxPlayers.ToString();
1494 new GUIButton(
new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight), style:
"GUIPlusButton", textAlignment: Alignment.Center)
1497 OnClicked = ChangeMaxPlayers,
1500 maxPlayersLabel.RectTransform.IsFixedSize =
true;
1502 label =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get(
"Password"), textAlignment: textAlignment);
1503 passwordBox =
new GUITextBox(
new RectTransform(textFieldSize, label.RectTransform,
Anchor.CenterRight), text: password, textAlignment: textAlignment)
1509 var languageLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform),
1510 TextManager.Get(
"Language"), textAlignment: textAlignment);
1511 languageDropdown =
new GUIDropDown(
new RectTransform(textFieldSize, languageLabel.RectTransform,
Anchor.CenterRight));
1512 foreach (var language
in ServerLanguageOptions.Options)
1514 languageDropdown.
AddItem(language.Label, language.Identifier);
1516 var defaultLanguage = ServerLanguageOptions.PickLanguage(GameSettings.CurrentConfig.Language);
1517 var settingsLanguage = serverSettings.GetAttributeIdentifier(
"language", defaultLanguage.Value).ToLanguageIdentifier();
1518 if (!ServerLanguageOptions.Options.Any(o => o.Identifier == settingsLanguage))
1520 settingsLanguage = defaultLanguage;
1522 languageDropdown.
Select(ServerLanguageOptions.Options.FindIndex(o => o.Identifier == settingsLanguage));
1524 var serverExecutableLabel =
new GUITextBlock(
new RectTransform(textLabelSize, parent.RectTransform),
1525 TextManager.Get(
"ServerExecutable"), textAlignment: textAlignment);
1526 const string vanillaServerOption =
"Vanilla";
1527 serverExecutableDropdown
1528 =
new GUIDropDown(
new RectTransform(textFieldSize, serverExecutableLabel.RectTransform,
Anchor.CenterRight),
1529 vanillaServerOption);
1532 serverExecutableDropdown.
AddItem(vanillaServerOption, userData:
null);
1533 serverExecutableDropdown.
OnSelected = (selected, userData) =>
1535 if (userData !=
null)
1537 var warningBox =
new GUIMessageBox(headerText: TextManager.Get(
"Warning"),
1538 text: TextManager.GetWithVariable(
"ModServerExesAtYourOwnRisk",
"[exename]", serverExecutableDropdown.
Text),
1539 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
1540 warningBox.Buttons[0].OnClicked = (_, __) =>
1545 warningBox.Buttons[1].OnClicked = (_, __) =>
1547 serverExecutableDropdown.
Select(0);
1553 serverExecutableDropdown.
Text = ToolBox.LimitString(serverExecutableDropdown.
Text,
1554 serverExecutableDropdown.
Font, serverExecutableDropdown.
Rect.Width * 8 / 10);
1558 serverExecutableLabel.RectTransform.IsFixedSize =
true;
1562 var tickboxAreaUpper =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal:
true);
1564 isPublicBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get(
"PublicServer"))
1567 ToolTip = TextManager.Get(
"PublicServerToolTip")
1570 wrongPasswordBanBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get(
"ServerSettingsBanAfterWrongPassword"))
1575 tickboxAreaUpper.RectTransform.IsFixedSize =
true;
1579 var tickboxAreaLower =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal:
true);
1581 karmaBox =
new GUITickBox(
new RectTransform(
new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get(
"HostServerKarmaSetting"))
1584 ToolTip = TextManager.Get(
"hostserverkarmasettingtooltip")
1587 tickboxAreaLower.RectTransform.IsFixedSize =
true;
1590 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.05f), content.RectTransform), style:
null);
1592 new GUIButton(
new RectTransform(
new Vector2(0.4f, 0.07f), content.RectTransform), TextManager.Get(
"StartServerButton"), style:
"GUIButtonLarge")
1594 OnClicked = (btn, userdata) =>
1601 void CheckServerName()
1603 string name = serverNameBox.
Text;
1604 if (
string.IsNullOrEmpty(name))
1606 serverNameBox.
Flash();
1609 if (isPublicBox.
Selected && ForbiddenWordFilter.IsForbidden(name, out
string forbiddenWord))
1611 var msgBox =
new GUIMessageBox(
"",
1612 TextManager.GetWithVariables(
"forbiddenservernameverification", (
"[forbiddenword]", forbiddenWord), (
"[servername]", name)),
1613 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1614 msgBox.Buttons[0].OnClicked += (_, __) =>
1620 msgBox.Buttons[1].OnClicked += msgBox.Close;
1626 void CheckServerExe()
1628 if (serverExecutableDropdown?.SelectedData is ServerExecutableFile serverExe &&
1629 serverExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
1631 var msgBox =
new GUIMessageBox(
string.Empty,
1632 TextManager.GetWithVariables(
"versionmismatchwarning",
1633 (
"[gameversion]", serverExe.ContentPackage.GameVersion.ToString()),
1634 (
"[contentversion]", GameMain.VanillaContent.GameVersion.ToString())) +
"\n\n"+
1635 TextManager.GetWithVariable(
"versionmismatch.verifylaunch",
"[exename]", serverExe.ContentPackage.Name),
1636 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") });
1637 msgBox.Buttons[0].OnClicked += (_, __) =>
1643 msgBox.Buttons[1].OnClicked += msgBox.Close;
1650 private void SetServerPlayStyle(
PlayStyle playStyle)
1652 playstyleBanner.
Sprite = GUIStyle
1653 .GetComponentStyle($
"PlayStyleBanner.{playStyle}")
1654 .GetSprite(GUIComponent.ComponentState.None);
1655 playstyleBanner.
UserData = playStyle;
1657 var nameText = playstyleBanner.
GetChild<GUITextBlock>();
1658 nameText.Text = TextManager.AddPunctuation(
':', TextManager.Get(
"serverplaystyle"), TextManager.Get(
"servertag." + playStyle));
1661 nameText.RectTransform.NonScaledSize = (nameText.Font.MeasureString(nameText.Text) +
new Vector2(25, 10) * GUI.Scale).ToPoint();
1663 playstyleDescription.
Text = TextManager.Get(
"servertagdescription." + playStyle);
1665 Alignment.CenterLeft : Alignment.Center;
1669 private void FetchRemoteContent()
1671 string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
1672 if (
string.IsNullOrEmpty(remoteContentUrl)) {
return; }
1675 var client =
new RestClient(remoteContentUrl);
1676 var request =
new RestRequest(
"MenuContent.xml", Method.GET);
1677 TaskPool.Add(
"RequestMainMenuRemoteContent", client.ExecuteAsync(request),
1678 RemoteContentReceived);
1684 DebugConsole.ThrowError(
"Fetching remote content to the main menu failed.", e);
1686 GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.FetchRemoteContent:Exception", GameAnalyticsManager.ErrorSeverity.Error,
1687 "Fetching remote content to the main menu failed. " + e.Message);
1692 private void RemoteContentReceived(Task t)
1696 if (!t.TryGetResult(out IRestResponse remoteContentResponse)) {
throw new Exception(
"Task did not return a valid result"); }
1697 if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
1699 DebugConsole.AddWarning(
1700 "Failed to receive remote main menu content. " +
1701 "There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
1702 $
"(error code: {remoteContentResponse.StatusCode})");
1705 string xml = remoteContentResponse.Content;
1706 int index = xml.IndexOf(
'<');
1707 if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
1708 if (!
string.IsNullOrWhiteSpace(xml))
1710 remoteContentDoc = XDocument.Parse(xml);
1711 foreach (var subElement
in remoteContentDoc?.Root.Elements())
1713 GUIComponent.FromXML(subElement.FromPackage(
null), remoteContentContainer.RectTransform);
1721 DebugConsole.ThrowError(
"Reading received remote main menu content failed.", e);
1723 GameAnalyticsManager.AddErrorEventOnce(
"MainMenuScreen.RemoteContentReceived:Exception", GameAnalyticsManager.ErrorSeverity.Error,
1724 "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
Triggers when some item is cliecked from the dropdown. Note that SelectedData is not set yet when thi...
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