LuaCs Settings no longer uses LuaCsConfig Cs Package is no longer is used for enabling CSharp Scripting (except for dedicated server, to not make those people confused) Added popups for when you have CSharp mods enabled but don't have CSharp enabled when going to singleplayer/multiplayer
1670 lines
77 KiB
C#
1670 lines
77 KiB
C#
//#define TEST_REMOTE_CONTENT
|
|
|
|
using Barotrauma.Extensions;
|
|
using Barotrauma.Networking;
|
|
using Barotrauma.Tutorials;
|
|
using Lidgren.Network;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using RestSharp;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Diagnostics;
|
|
using Barotrauma.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
using Barotrauma.Steam;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
class MainMenuScreen : Screen
|
|
{
|
|
private enum Tab
|
|
{
|
|
NewGame = 0,
|
|
LoadGame = 1,
|
|
HostServer = 2,
|
|
Settings = 3,
|
|
Tutorials = 4,
|
|
JoinServer = 5,
|
|
CharacterEditor = 6,
|
|
SubmarineEditor = 7,
|
|
SteamWorkshop = 8,
|
|
Credits = 9,
|
|
Empty = 10
|
|
}
|
|
|
|
private readonly GUIComponent buttonsParent;
|
|
|
|
private readonly Dictionary<Tab, GUIFrame> menuTabs;
|
|
|
|
private SinglePlayerCampaignSetupUI campaignSetupUI;
|
|
|
|
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
|
|
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
|
|
private GUIDropDown languageDropdown, serverExecutableDropdown;
|
|
private readonly GUIButton joinServerButton, hostServerButton;
|
|
|
|
private readonly GUIFrame modsButtonContainer;
|
|
private readonly GUIButton modsButton, modUpdatesButton;
|
|
private (DateTime WhenToRefresh, int Count) modUpdateStatus = (DateTime.Now, 0);
|
|
private static readonly TimeSpan ModUpdateInterval = TimeSpan.FromSeconds(60.0f);
|
|
|
|
private readonly GameMain game;
|
|
|
|
private GUIImage playstyleBanner;
|
|
private GUITextBlock playstyleDescription;
|
|
|
|
private readonly GUIComponent remoteContentContainer;
|
|
private XDocument remoteContentDoc;
|
|
|
|
private Tab selectedTab = Tab.Empty;
|
|
|
|
private Sprite backgroundSprite;
|
|
|
|
private readonly GUIComponent titleText;
|
|
|
|
private readonly CreditsPlayer creditsPlayer;
|
|
|
|
public static readonly Queue<ulong> WorkshopItemsToUpdate = new Queue<ulong>();
|
|
|
|
private GUIImage tutorialBanner;
|
|
private GUITextBlock tutorialHeader, tutorialDescription;
|
|
private GUIListBox tutorialList;
|
|
|
|
private GUIComponent versionMismatchWarning;
|
|
|
|
#region Creation
|
|
public MainMenuScreen(GameMain game)
|
|
{
|
|
GameMain.Instance.ResolutionChanged += () =>
|
|
{
|
|
SetMenuTabPositioning();
|
|
CreateHostServerFields();
|
|
SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
|
|
if (remoteContentDoc?.Root != null)
|
|
{
|
|
remoteContentContainer.ClearChildren();
|
|
try
|
|
{
|
|
foreach (var subElement in remoteContentDoc.Root.Elements())
|
|
{
|
|
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
#if DEBUG
|
|
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
|
|
#endif
|
|
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.RemoteContentParse:Exception", GameAnalyticsManager.ErrorSeverity.Error,
|
|
"Reading received remote main menu content failed. " + e.Message);
|
|
}
|
|
}
|
|
};
|
|
|
|
versionMismatchWarning = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.065f), Frame.RectTransform) { AbsoluteOffset = new Point(GUI.IntScale(15)) }, style: "InnerFrame", color: GUIStyle.Red)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
Visible = false
|
|
};
|
|
var versionMismatchContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), versionMismatchWarning.RectTransform, Anchor.Center), isHorizontal: true)
|
|
{
|
|
RelativeSpacing = 0.05f,
|
|
};
|
|
new GUIImage(new RectTransform(new Vector2(1.0f), versionMismatchContent.RectTransform, scaleBasis: ScaleBasis.Smallest), style: "GUINotificationButton")
|
|
{
|
|
Color = GUIStyle.Orange
|
|
};
|
|
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1.0f), versionMismatchContent.RectTransform),
|
|
TextManager.GetWithVariables("versionmismatchwarning",
|
|
("[gameversion]", GameMain.Version.ToString()),
|
|
("[contentversion]", ContentPackageManager.VanillaCorePackage.GameVersion.ToString())),
|
|
wrap: true)
|
|
{
|
|
TextColor = GUIStyle.Orange
|
|
};
|
|
|
|
new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
|
|
{ RelativeOffset = new Vector2(0.08f, 0.05f), AbsoluteOffset = new Point(-8, -8) },
|
|
style: "TitleText")
|
|
{
|
|
Color = Color.Black * 0.5f,
|
|
CanBeFocused = false
|
|
};
|
|
titleText = new GUIImage(new RectTransform(new Vector2(0.4f, 0.25f), Frame.RectTransform, Anchor.BottomRight)
|
|
{ RelativeOffset = new Vector2(0.08f, 0.05f) },
|
|
style: "TitleText");
|
|
|
|
buttonsParent = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.85f), parent: Frame.RectTransform, anchor: Anchor.CenterLeft)
|
|
{
|
|
AbsoluteOffset = new Point(50, 0)
|
|
})
|
|
{
|
|
Stretch = true,
|
|
RelativeSpacing = 0.02f
|
|
};
|
|
|
|
remoteContentContainer = new GUIFrame(new RectTransform(Vector2.One, parent: Frame.RectTransform), style: null)
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
#if TEST_REMOTE_CONTENT
|
|
|
|
var doc = XMLExtensions.TryLoadXml("Content/UI/MenuContent.xml");
|
|
if (doc?.Root != null)
|
|
{
|
|
foreach (var subElement in doc?.Root.Elements())
|
|
{
|
|
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
|
|
}
|
|
}
|
|
#else
|
|
SpamServerFilters.RequestGlobalSpamFilter();
|
|
FetchRemoteContent();
|
|
#endif
|
|
|
|
float labelHeight = 0.18f;
|
|
|
|
|
|
// === CAMPAIGN
|
|
var campaignHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.1f, 0.0f) }, isHorizontal: true);
|
|
|
|
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), campaignHolder.RectTransform), "MainMenuCampaignIcon")
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.0f), campaignHolder.RectTransform), style: null);
|
|
|
|
var campaignNavigation = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.75f), parent: campaignHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
|
|
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, labelHeight), campaignNavigation.RectTransform),
|
|
TextManager.Get("CampaignLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style: "MainMenuGUITextBlock") { ForceUpperCase = ForceUpperCase.Yes };
|
|
|
|
var campaignButtons = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), parent: campaignNavigation.RectTransform), style: "MainMenuGUIFrame");
|
|
|
|
var campaignList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.2f), parent: campaignButtons.RectTransform))
|
|
{
|
|
Stretch = false,
|
|
RelativeSpacing = 0.035f
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get("TutorialButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.Tutorials,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get("LoadGameButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.LoadGame,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), campaignList.RectTransform), TextManager.Get("NewGameButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.NewGame,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// === MULTIPLAYER
|
|
var multiplayerHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.05f, 0.0f) }, isHorizontal: true);
|
|
|
|
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), multiplayerHolder.RectTransform), "MainMenuMultiplayerIcon")
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.0f), multiplayerHolder.RectTransform), style: null);
|
|
|
|
var multiplayerNavigation = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.75f), parent: multiplayerHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
|
|
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, labelHeight), multiplayerNavigation.RectTransform),
|
|
TextManager.Get("MultiplayerLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style: "MainMenuGUITextBlock") { ForceUpperCase = ForceUpperCase.Yes };
|
|
|
|
var multiplayerButtons = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), parent: multiplayerNavigation.RectTransform), style: "MainMenuGUIFrame");
|
|
|
|
var multiplayerList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.2f), parent: multiplayerButtons.RectTransform))
|
|
{
|
|
Stretch = false,
|
|
RelativeSpacing = 0.035f
|
|
};
|
|
|
|
joinServerButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), multiplayerList.RectTransform), TextManager.Get("JoinServerButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.JoinServer,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
hostServerButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), multiplayerList.RectTransform), TextManager.Get("HostServerButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.HostServer,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// === CUSTOMIZE
|
|
var customizeHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), parent: buttonsParent.RectTransform) { RelativeOffset = new Vector2(0.15f, 0.0f) }, isHorizontal: true);
|
|
|
|
new GUIImage(new RectTransform(new Vector2(0.2f, 0.7f), customizeHolder.RectTransform), "MainMenuCustomizeIcon")
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.0f), customizeHolder.RectTransform), style: null);
|
|
|
|
var customizeNavigation = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.75f), parent: customizeHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.25f) });
|
|
|
|
new GUITextBlock(new RectTransform(new Vector2(1.0f, labelHeight), customizeNavigation.RectTransform),
|
|
TextManager.Get("CustomizeLabel"), textAlignment: Alignment.Left, font: GUIStyle.LargeFont, textColor: Color.Black, style: "MainMenuGUITextBlock") { ForceUpperCase = ForceUpperCase.Yes };
|
|
|
|
var customizeButtons = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), parent: customizeNavigation.RectTransform), style: "MainMenuGUIFrame");
|
|
|
|
var customizeList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.2f), parent: customizeButtons.RectTransform))
|
|
{
|
|
Stretch = false,
|
|
RelativeSpacing = 0.035f
|
|
};
|
|
|
|
modsButtonContainer = new GUIFrame(new RectTransform(Vector2.One, customizeList.RectTransform),
|
|
style: null);
|
|
|
|
modsButton = new GUIButton(new RectTransform(Vector2.One, modsButtonContainer.RectTransform),
|
|
TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
Enabled = true,
|
|
UserData = Tab.SteamWorkshop,
|
|
OnClicked = SelectTab
|
|
};
|
|
|
|
modUpdatesButton = new GUIButton(new RectTransform(Vector2.One * 0.95f, modsButtonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight),
|
|
style: "GUIUpdateButton")
|
|
{
|
|
ToolTip = TextManager.Get("ModUpdatesAvailable"),
|
|
OnClicked = (_, _) =>
|
|
{
|
|
BulkDownloader.PrepareUpdates();
|
|
return false;
|
|
},
|
|
Visible = false
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.SubmarineEditor,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("CharacterEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.CharacterEditor,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// === OPTION
|
|
var optionHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), parent: buttonsParent.RectTransform), isHorizontal: true);
|
|
|
|
new GUIImage(new RectTransform(new Vector2(0.15f, 0.6f), optionHolder.RectTransform), "MainMenuOptionIcon")
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(0.01f, 0.0f), optionHolder.RectTransform), style: null);
|
|
|
|
var optionButtons = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), parent: optionHolder.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.0f) });
|
|
|
|
var optionList = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.25f), parent: optionButtons.RectTransform))
|
|
{
|
|
Stretch = false,
|
|
RelativeSpacing = 0.035f
|
|
};
|
|
|
|
var settingsButtonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), style: null);
|
|
|
|
new GUIButton(new RectTransform(Vector2.One, settingsButtonContainer.RectTransform), TextManager.Get("SettingsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.Settings,
|
|
OnClicked = SelectTab
|
|
};
|
|
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("EditorDisclaimerWikiLink"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
OnClicked = (button, userData) =>
|
|
{
|
|
string url = TextManager.Get("EditorDisclaimerWikiUrl").Fallback("https://barotraumagame.com/wiki").Value;
|
|
GameMain.ShowOpenUrlInWebBrowserPrompt(url, promptExtensionTag: "wikinotice");
|
|
return true;
|
|
}
|
|
};
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("CreditsButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
UserData = Tab.Credits,
|
|
OnClicked = SelectTab
|
|
};
|
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), optionList.RectTransform), TextManager.Get("QuitButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
|
{
|
|
ForceUpperCase = ForceUpperCase.Yes,
|
|
OnClicked = QuitClicked
|
|
};
|
|
|
|
//debug button for quickly starting a new round
|
|
#if DEBUG
|
|
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(40, 80) },
|
|
"Quickstart (dev)", style: "GUIButtonLarge", color: GUIStyle.Red)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
UserData = Tab.Empty,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
|
|
QuickStart();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(40, 130) },
|
|
"Profiling", style: "GUIButtonLarge", color: GUIStyle.Red)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
UserData = Tab.Empty,
|
|
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.",
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
|
|
QuickStart(fixedSeed: true);
|
|
GameMain.ShowPerf = true;
|
|
GameMain.ShowFPS = true;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(40, 180) },
|
|
"Join Localhost", style: "GUIButtonLarge", color: GUIStyle.Red)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
UserData = Tab.Empty,
|
|
ToolTip = "Connects to a locally hosted dedicated server, assuming default port.",
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
SelectTab(tb, userdata);
|
|
|
|
GameMain.Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(SteamManager.GetUsername()),
|
|
new LidgrenEndpoint(IPAddress.Loopback, NetConfig.DefaultPort), "localhost", Option<int>.None());
|
|
|
|
return true;
|
|
}
|
|
};
|
|
#endif
|
|
new GUIButton(new RectTransform(new Point(300, 30), Frame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(40, 50) },
|
|
$"Open LuaCs Settings", style: "MainMenuGUIButton", color: GUIStyle.Red)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
OnClicked = (tb, userdata) =>
|
|
{
|
|
LuaCsSettingsMenu.Open(Frame.RectTransform);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
string version = File.Exists(LuaCsSetup.VersionFile) ? File.ReadAllText(LuaCsSetup.VersionFile) : "Github";
|
|
|
|
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)
|
|
{
|
|
IgnoreLayoutGroups = false
|
|
};
|
|
|
|
var minButtonSize = new Point(120, 20);
|
|
var maxButtonSize = new Point(480, 80);
|
|
|
|
var relativeSize = new Vector2(0.6f, 0.65f);
|
|
var minSize = new Point(600, 400);
|
|
var maxSize = new Point(2000, 1500);
|
|
var anchor = Anchor.Center;
|
|
var pivot = Pivot.Center;
|
|
Vector2 relativeOffset = new Vector2(0.05f, 0.0f);
|
|
|
|
menuTabs = new Dictionary<Tab, GUIFrame>
|
|
{
|
|
[Tab.Settings] = new GUIFrame(new RectTransform(new Vector2(relativeSize.X, 0.8f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset },
|
|
style: null)
|
|
{
|
|
CanBeFocused = false
|
|
},
|
|
[Tab.NewGame] = new GUIFrame(new RectTransform(relativeSize * new Vector2(1.0f, 1.15f), GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset }),
|
|
[Tab.LoadGame] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset })
|
|
};
|
|
|
|
CreateCampaignSetupUI();
|
|
|
|
var hostServerScale = new Vector2(0.7f, 1.2f);
|
|
menuTabs[Tab.HostServer] = new GUIFrame(new RectTransform(
|
|
Vector2.Multiply(relativeSize, hostServerScale), GUI.Canvas, anchor, pivot, minSize.Multiply(hostServerScale), maxSize.Multiply(hostServerScale))
|
|
{ RelativeOffset = relativeOffset });
|
|
|
|
CreateHostServerFields();
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
menuTabs[Tab.Tutorials] = new GUIFrame(new RectTransform(relativeSize, GUI.Canvas, anchor, pivot, minSize, maxSize) { RelativeOffset = relativeOffset });
|
|
CreateTutorialTab();
|
|
|
|
this.game = game;
|
|
|
|
menuTabs[Tab.Credits] = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null)
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, menuTabs[Tab.Credits].RectTransform, Anchor.Center), style: "GUIBackgroundBlocker")
|
|
{
|
|
CanBeFocused = false
|
|
};
|
|
|
|
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);
|
|
creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml");
|
|
creditsPlayer.CloseButton.OnClicked = (btn, userdata) =>
|
|
{
|
|
SelectTab(Tab.Empty);
|
|
return true;
|
|
};
|
|
|
|
SetMenuTabPositioning();
|
|
}
|
|
|
|
private void SetMenuTabPositioning()
|
|
{
|
|
foreach (GUIFrame menuTab in menuTabs.Values)
|
|
{
|
|
var anchor = GUI.IsUltrawide ? Anchor.Center : Anchor.CenterRight;
|
|
var pivot = GUI.IsUltrawide ? Pivot.Center : Pivot.CenterRight;
|
|
Vector2 relativeOffset = GUI.IsUltrawide ? Vector2.Zero : new Vector2(0.05f, 0.0f);
|
|
menuTab.RectTransform.SetPosition(anchor, pivot);
|
|
menuTab.RectTransform.RelativeOffset = relativeOffset;
|
|
}
|
|
}
|
|
|
|
private void CreateTutorialTab()
|
|
{
|
|
var tutorialInnerFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[Tab.Tutorials].RectTransform, Anchor.Center), style: "InnerFrame");
|
|
var tutorialContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), tutorialInnerFrame.RectTransform, Anchor.Center), isHorizontal: true) { RelativeSpacing = 0.02f, Stretch = true };
|
|
|
|
tutorialList = new GUIListBox(new RectTransform(new Vector2(0.4f, 1.0f), tutorialContent.RectTransform))
|
|
{
|
|
PlaySoundOnSelect = true,
|
|
OnSelected = (component, obj) =>
|
|
{
|
|
SelectTutorial(obj as Tutorial);
|
|
return true;
|
|
}
|
|
};
|
|
var tutorialPreview = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), tutorialContent.RectTransform)) { RelativeSpacing = 0.05f, Stretch = true };
|
|
var imageContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style: "InnerFrame");
|
|
tutorialBanner = new GUIImage(new RectTransform(Vector2.One, imageContainer.RectTransform), style: null, scaleToFit: true);
|
|
|
|
var infoContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.5f), tutorialPreview.RectTransform), style: "GUIFrameListBox");
|
|
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), infoContainer.RectTransform, Anchor.Center), childAnchor: Anchor.TopLeft)
|
|
{
|
|
AbsoluteSpacing = GUI.IntScale(10)
|
|
};
|
|
|
|
tutorialHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), string.Empty, font: GUIStyle.SubHeadingFont);
|
|
tutorialDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), string.Empty, wrap: true);
|
|
|
|
var startButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.0f), infoContent.RectTransform, Anchor.BottomRight), text: TextManager.Get("startgamebutton"))
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
OnClicked = (component, obj) =>
|
|
{
|
|
(tutorialList.SelectedData as Tutorial)?.Start();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
Tutorial firstTutorial = null;
|
|
foreach (var tutorialPrefab in TutorialPrefab.Prefabs.OrderBy(p => p.Order))
|
|
{
|
|
var tutorial = new Tutorial(tutorialPrefab);
|
|
firstTutorial ??= tutorial;
|
|
var tutorialText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), tutorialList.Content.RectTransform), tutorial.DisplayName)
|
|
{
|
|
Padding = new Vector4(30.0f * GUI.Scale, 0,0,0),
|
|
UserData = tutorial
|
|
};
|
|
tutorialText.RectTransform.MinSize = new Point(0, (int)(tutorialText.TextSize.Y * 2));
|
|
}
|
|
GUITextBlock.AutoScaleAndNormalize(tutorialList.Content.Children.Select(c => c as GUITextBlock));
|
|
tutorialList.Select(firstTutorial);
|
|
}
|
|
|
|
private void SelectTutorial(Tutorial tutorial)
|
|
{
|
|
tutorialHeader.Text = tutorial.DisplayName;
|
|
tutorialHeader.CalculateHeightFromText();
|
|
tutorialDescription.Text = tutorial.Description;
|
|
tutorialDescription.CalculateHeightFromText();
|
|
(tutorialDescription.Parent as GUILayoutGroup)?.Recalculate();
|
|
tutorial.TutorialPrefab.Banner?.EnsureLazyLoaded();
|
|
tutorialBanner.Sprite = tutorial.TutorialPrefab.Banner;
|
|
tutorialBanner.Color = tutorial.TutorialPrefab.Banner == null ? Color.Black : Color.White;
|
|
}
|
|
|
|
public static void UpdateInstanceTutorialButtons()
|
|
{
|
|
if (GameMain.MainMenuScreen is not MainMenuScreen menuScreen) { return; }
|
|
menuScreen.tutorialList.ClearChildren();
|
|
menuScreen.CreateTutorialTab();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Selection
|
|
public override void Select()
|
|
{
|
|
GameMain.LuaCs.Stop();
|
|
|
|
ResetModUpdateButton();
|
|
|
|
if (WorkshopItemsToUpdate.Any())
|
|
{
|
|
while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
|
|
{
|
|
SteamManager.Workshop.OnItemDownloadComplete(workshopId, forceInstall: true);
|
|
}
|
|
}
|
|
|
|
GUI.PreventPauseMenuToggle = false;
|
|
|
|
base.Select();
|
|
|
|
if (GameMain.Client != null)
|
|
{
|
|
GameMain.Client.Quit();
|
|
GameMain.Client = null;
|
|
}
|
|
|
|
GameMain.SubEditorScreen?.ClearBackedUpSubInfo();
|
|
Submarine.Unload();
|
|
|
|
versionMismatchWarning.Visible = GameMain.Version < ContentPackageManager.VanillaCorePackage.GameVersion;
|
|
|
|
ResetButtonStates(null);
|
|
}
|
|
|
|
public override void Deselect()
|
|
{
|
|
base.Deselect();
|
|
SelectTab(null, 0);
|
|
}
|
|
|
|
private bool SelectTab(GUIButton button, object obj)
|
|
{
|
|
titleText.Visible = true;
|
|
if (obj is Tab tab)
|
|
{
|
|
SelectTab(tab);
|
|
}
|
|
else
|
|
{
|
|
SelectTab(Tab.Empty);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool SelectTab(Tab tab)
|
|
{
|
|
titleText.Visible = true;
|
|
SettingsMenu.Instance?.Close();
|
|
#warning TODO: reimplement settings confirmation dialog
|
|
|
|
switch (tab)
|
|
{
|
|
case Tab.NewGame:
|
|
if (GameSettings.CurrentConfig.TutorialSkipWarning)
|
|
{
|
|
selectedTab = Tab.Empty;
|
|
ShowTutorialSkipWarning(Tab.NewGame);
|
|
return true;
|
|
}
|
|
campaignSetupUI.RandomizeCrew();
|
|
campaignSetupUI.SetPage(0);
|
|
campaignSetupUI.CreateDefaultSaveName();
|
|
campaignSetupUI.RandomizeSeed();
|
|
campaignSetupUI.UpdateSubList(SubmarineInfo.SavedSubmarines);
|
|
break;
|
|
case Tab.LoadGame:
|
|
campaignSetupUI.CreateLoadMenu();
|
|
break;
|
|
case Tab.Settings:
|
|
SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
|
|
break;
|
|
case Tab.JoinServer:
|
|
if (GameSettings.CurrentConfig.TutorialSkipWarning)
|
|
{
|
|
selectedTab = Tab.Empty;
|
|
ShowTutorialSkipWarning(Tab.JoinServer);
|
|
return true;
|
|
}
|
|
GameMain.ServerListScreen.Select();
|
|
break;
|
|
case Tab.HostServer:
|
|
if (GameSettings.CurrentConfig.TutorialSkipWarning)
|
|
{
|
|
selectedTab = Tab.Empty;
|
|
ShowTutorialSkipWarning(tab);
|
|
return true;
|
|
}
|
|
serverExecutableDropdown.ListBox.Content.Children.ToArray()
|
|
.Where(c => c.UserData is ServerExecutableFile f && !ContentPackageManager.EnabledPackages.All.Contains(f.ContentPackage))
|
|
.ForEach(serverExecutableDropdown.ListBox.RemoveChild);
|
|
var newServerExes
|
|
= ContentPackageManager.EnabledPackages.All.SelectMany(p => p.GetFiles<ServerExecutableFile>())
|
|
.Where(f => serverExecutableDropdown.ListBox.Content.Children.None(c => c.UserData == f))
|
|
.ToArray();
|
|
foreach (var newServerExe in newServerExes)
|
|
{
|
|
var serverExeEntry = serverExecutableDropdown.AddItem($"{newServerExe.ContentPackage.Name} - {Path.GetFileNameWithoutExtension(newServerExe.Path.Value)}", userData: newServerExe);
|
|
if (newServerExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
|
|
{
|
|
serverExeEntry.ToolTip =
|
|
TextManager.GetWithVariables("versionmismatchwarning",
|
|
("[gameversion]", newServerExe.ContentPackage.GameVersion.ToString()),
|
|
("[contentversion]", GameMain.VanillaContent.GameVersion.ToString()));
|
|
if (serverExeEntry is GUITextBlock serverExeText)
|
|
{
|
|
serverExeText.TextColor = GUIStyle.Red;
|
|
}
|
|
}
|
|
}
|
|
serverExecutableDropdown.ListBox.Content.Children.ForEach(c =>
|
|
{
|
|
c.RectTransform.RelativeSize = (1.0f, c.RectTransform.RelativeSize.Y);
|
|
c.ForceLayoutRecalculation();
|
|
});
|
|
bool serverExePickable = serverExecutableDropdown.ListBox.Content.CountChildren > 1;
|
|
bool wasPickable = serverExecutableDropdown.Parent.Visible;
|
|
if (wasPickable != serverExePickable)
|
|
{
|
|
serverExecutableDropdown.Parent.Visible = serverExePickable;
|
|
serverExecutableDropdown.Parent.IgnoreLayoutGroups = !serverExePickable;
|
|
(serverExecutableDropdown.Parent.Parent as GUILayoutGroup)?.Recalculate();
|
|
if (serverExecutableDropdown.SelectedComponent is null)
|
|
{
|
|
serverExecutableDropdown.Select(0);
|
|
}
|
|
}
|
|
break;
|
|
case Tab.Tutorials:
|
|
UpdateTutorialList();
|
|
break;
|
|
case Tab.CharacterEditor:
|
|
Submarine.MainSub = null;
|
|
CoroutineManager.StartCoroutine(SelectScreenWithWaitCursor(GameMain.CharacterEditorScreen));
|
|
break;
|
|
case Tab.SubmarineEditor:
|
|
CoroutineManager.StartCoroutine(SelectScreenWithWaitCursor(GameMain.SubEditorScreen));
|
|
break;
|
|
case Tab.SteamWorkshop:
|
|
var settings = SettingsMenu.Create(menuTabs[Tab.Settings].RectTransform);
|
|
settings.SelectTab(SettingsMenu.Tab.Mods);
|
|
tab = Tab.Settings;
|
|
break;
|
|
case Tab.Credits:
|
|
titleText.Visible = false;
|
|
creditsPlayer.Restart();
|
|
break;
|
|
case Tab.Empty:
|
|
titleText.Visible = true;
|
|
selectedTab = Tab.Empty;
|
|
break;
|
|
}
|
|
|
|
selectedTab = tab;
|
|
|
|
return true;
|
|
}
|
|
|
|
private IEnumerable<CoroutineStatus> SelectScreenWithWaitCursor(Screen screen)
|
|
{
|
|
GUI.SetCursorWaiting();
|
|
//tiny delay to get the cursor to render
|
|
yield return new WaitForSeconds(0.02f);
|
|
GUI.ClearCursorWait();
|
|
screen.Select();
|
|
yield return CoroutineStatus.Success;
|
|
}
|
|
|
|
public bool ReturnToMainMenu(GUIButton button, object obj)
|
|
{
|
|
GUI.PreventPauseMenuToggle = false;
|
|
|
|
if (Selected != this)
|
|
{
|
|
Select();
|
|
}
|
|
else
|
|
{
|
|
ResetButtonStates(button);
|
|
}
|
|
|
|
SelectTab(null, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void ResetButtonStates(GUIButton button)
|
|
{
|
|
foreach (GUIComponent child in buttonsParent.Children)
|
|
{
|
|
GUIButton otherButton = child as GUIButton;
|
|
if (otherButton == null || otherButton == button) continue;
|
|
|
|
otherButton.Selected = false;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public void ResetModUpdateButton()
|
|
{
|
|
modUpdateStatus = (DateTime.Now, 0);
|
|
modUpdatesButton.Visible = false;
|
|
}
|
|
|
|
public void QuickStart(bool fixedSeed = false, Identifier sub = default, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
|
|
{
|
|
if (fixedSeed)
|
|
{
|
|
Rand.SetSyncedSeed(1);
|
|
Rand.SetLocalRandom(1);
|
|
}
|
|
|
|
SubmarineInfo selectedSub = null;
|
|
Identifier subName = sub.IfEmpty(GameSettings.CurrentConfig.QuickStartSub);
|
|
if (!subName.IsEmpty)
|
|
{
|
|
DebugConsole.NewMessage($"Loading the predefined quick start sub \"{subName}\"", Color.White);
|
|
|
|
selectedSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
|
|
if (selectedSub == null)
|
|
{
|
|
DebugConsole.NewMessage($"Cannot find a sub that matches the name \"{subName}\".", Color.Red);
|
|
}
|
|
}
|
|
if (selectedSub == null)
|
|
{
|
|
DebugConsole.NewMessage("Loading a random sub.", Color.White);
|
|
var subs = SubmarineInfo.SavedSubmarines.Where(s => s.Type == SubmarineType.Player && !s.HasTag(SubmarineTag.Shuttle) && !s.HasTag(SubmarineTag.HideInMenus));
|
|
selectedSub = subs.ElementAt(Rand.Int(subs.Count()));
|
|
}
|
|
var gamesession = new GameSession(
|
|
selectedSub,
|
|
GameModePreset.DevSandbox,
|
|
missionPrefabs: null);
|
|
//(gamesession.GameMode as SinglePlayerCampaign).GenerateMap(ToolBox.RandomSeed(8));
|
|
gamesession.StartRound(fixedSeed ? "abcd" : ToolBox.RandomSeed(8), difficulty, levelGenerationParams);
|
|
GameMain.GameScreen.Select();
|
|
// TODO: modding support
|
|
Identifier[] jobIdentifiers = new Identifier[] {
|
|
"captain".ToIdentifier(),
|
|
"engineer".ToIdentifier(),
|
|
"mechanic".ToIdentifier(),
|
|
"securityofficer".ToIdentifier(),
|
|
"medicaldoctor".ToIdentifier() };
|
|
foreach (Identifier job in jobIdentifiers)
|
|
{
|
|
var jobPrefab = JobPrefab.Get(job);
|
|
var variant = Rand.Range(0, jobPrefab.Variants);
|
|
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant);
|
|
if (characterInfo.Job == null)
|
|
{
|
|
DebugConsole.ThrowError("Failed to find the job \"" + job + "\"!");
|
|
}
|
|
gamesession.CrewManager.AddCharacterInfo(characterInfo);
|
|
}
|
|
gamesession.CrewManager.InitSinglePlayerRound();
|
|
}
|
|
|
|
private void ShowTutorialSkipWarning(Tab tabToContinueTo)
|
|
{
|
|
var tutorialSkipWarning = new GUIMessageBox("", TextManager.Get("tutorialskipwarning"), new LocalizedString[] { TextManager.Get("tutorialwarningskiptutorials"), TextManager.Get("tutorialwarningplaytutorials") });
|
|
|
|
GUIButton.OnClickedHandler proceedToTab(Tab tab)
|
|
=> (btn, userdata) =>
|
|
{
|
|
var config = GameSettings.CurrentConfig;
|
|
config.TutorialSkipWarning = false;
|
|
GameSettings.SetCurrentConfig(config);
|
|
GameSettings.SaveCurrentConfig();
|
|
tutorialSkipWarning.Close();
|
|
SelectTab(tab);
|
|
return true;
|
|
};
|
|
|
|
tutorialSkipWarning.Buttons[0].OnClicked += proceedToTab(tabToContinueTo);
|
|
tutorialSkipWarning.Buttons[1].OnClicked += proceedToTab(Tab.Tutorials);
|
|
}
|
|
|
|
private void UpdateTutorialList()
|
|
{
|
|
foreach (GUITextBlock tutorialText in tutorialList.Content.Children)
|
|
{
|
|
var tutorial = (Tutorial)tutorialText.UserData;
|
|
if (CompletedTutorials.Instance.Contains(tutorial.Identifier) && tutorialText.GetChild<GUIImage>() == null)
|
|
{
|
|
new GUIImage(new RectTransform(new Point((int)(tutorialText.Padding.X * 0.8f)), tutorialText.RectTransform, Anchor.CenterLeft), style: "ObjectiveIndicatorCompleted");
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool ChangeMaxPlayers(GUIButton button, object obj)
|
|
{
|
|
int.TryParse(maxPlayersBox.Text, out int currMaxPlayers);
|
|
currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers + (int)button.UserData, 1, NetConfig.MaxPlayers);
|
|
maxPlayersBox.Text = currMaxPlayers.ToString();
|
|
return true;
|
|
}
|
|
|
|
private void TryStartServer()
|
|
{
|
|
if (SubmarineInfo.SavedSubmarines.Any(s => s.CalculatingHash))
|
|
{
|
|
var waitBox = new GUIMessageBox(TextManager.Get("pleasewait"), TextManager.Get("waitforsubmarinehashcalculations"), new LocalizedString[] { TextManager.Get("cancel") });
|
|
var waitCoroutine = CoroutineManager.StartCoroutine(WaitForSubmarineHashCalculations(waitBox), "WaitForSubmarineHashCalculations");
|
|
waitBox.Buttons[0].OnClicked += (btn, userdata) =>
|
|
{
|
|
CoroutineManager.StopCoroutines(waitCoroutine);
|
|
return true;
|
|
};
|
|
}
|
|
else
|
|
{
|
|
StartServer();
|
|
}
|
|
}
|
|
|
|
private IEnumerable<CoroutineStatus> WaitForSubmarineHashCalculations(GUIMessageBox messageBox)
|
|
{
|
|
LocalizedString originalText = messageBox.Text.Text;
|
|
int doneCount = 0;
|
|
do
|
|
{
|
|
doneCount = SubmarineInfo.SavedSubmarines.Count(s => !s.CalculatingHash);
|
|
messageBox.Text.Text = originalText + $" ({doneCount}/{SubmarineInfo.SavedSubmarines.Count()})";
|
|
yield return CoroutineStatus.Running;
|
|
} while (doneCount < SubmarineInfo.SavedSubmarines.Count());
|
|
messageBox.Close();
|
|
StartServer();
|
|
yield return CoroutineStatus.Success;
|
|
}
|
|
|
|
private void StartServer()
|
|
{
|
|
string name = serverNameBox.Text;
|
|
|
|
GameMain.ResetNetLobbyScreen();
|
|
try
|
|
{
|
|
string fileName;
|
|
if (serverExecutableDropdown.SelectedComponent?.UserData is ServerExecutableFile f &&
|
|
f.ContentPackage != GameMain.VanillaContent)
|
|
{
|
|
fileName = Path.Combine(
|
|
Path.GetDirectoryName(f.Path.Value),
|
|
Path.GetFileNameWithoutExtension(f.Path.Value));
|
|
#if WINDOWS
|
|
fileName += ".exe";
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if WINDOWS
|
|
fileName = "DedicatedServer.exe";
|
|
#else
|
|
fileName = "./DedicatedServer";
|
|
#endif
|
|
}
|
|
|
|
string arguments =
|
|
"-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
|
|
" -public " + isPublicBox.Selected.ToString() +
|
|
" -playstyle " + ((PlayStyle)playstyleBanner.UserData).ToString() +
|
|
" -banafterwrongpassword " + wrongPasswordBanBox.Selected.ToString() +
|
|
" -karmaenabled " + (!karmaBox.Selected).ToString() +
|
|
" -maxplayers " + maxPlayersBox.Text +
|
|
$" -language \"{(LanguageIdentifier)languageDropdown.SelectedData}\"";
|
|
|
|
if (!string.IsNullOrWhiteSpace(passwordBox.Text))
|
|
{
|
|
arguments += " -password \"" + ToolBox.EscapeCharacters(passwordBox.Text) + "\"";
|
|
}
|
|
else
|
|
{
|
|
arguments += " -nopassword";
|
|
}
|
|
|
|
if (SteamManager.GetSteamId().TryUnwrap(out var steamId1))
|
|
{
|
|
arguments += " -steamid " + steamId1.Value;
|
|
}
|
|
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
|
|
arguments += " -ownerkey " + ownerKey;
|
|
|
|
var processInfo = new ProcessStartInfo
|
|
{
|
|
FileName = fileName,
|
|
Arguments = arguments,
|
|
WorkingDirectory = Directory.GetCurrentDirectory(),
|
|
#if !DEBUG
|
|
CreateNoWindow = true,
|
|
UseShellExecute = false,
|
|
WindowStyle = ProcessWindowStyle.Hidden
|
|
#endif
|
|
};
|
|
ChildServerRelay.Start(processInfo);
|
|
Thread.Sleep(1000); //wait until the server is ready before connecting
|
|
|
|
GameMain.Client = new GameClient(MultiplayerPreferences.Instance.PlayerName.FallbackNullOrEmpty(
|
|
SteamManager.GetUsername().FallbackNullOrEmpty(name)),
|
|
SteamManager.GetSteamId().TryUnwrap(out var steamId)
|
|
? new SteamP2PEndpoint(steamId)
|
|
: (Endpoint)new LidgrenEndpoint(IPAddress.Loopback, NetConfig.DefaultPort),
|
|
name,
|
|
Option<int>.Some(ownerKey));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugConsole.ThrowError("Failed to start server", e);
|
|
}
|
|
}
|
|
|
|
private bool QuitClicked(GUIButton button, object obj)
|
|
{
|
|
game.Exit();
|
|
return true;
|
|
}
|
|
|
|
public override void AddToGUIUpdateList()
|
|
{
|
|
Frame.AddToGUIUpdateList();
|
|
if (selectedTab < Tab.Empty && menuTabs.TryGetValue(selectedTab, out GUIFrame tab) && tab != null)
|
|
{
|
|
tab.AddToGUIUpdateList();
|
|
switch (selectedTab)
|
|
{
|
|
case Tab.NewGame:
|
|
campaignSetupUI.CharacterMenus?.ForEach(m => m.AddToGUIUpdateList());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateOutOfDateWorkshopItemCount()
|
|
{
|
|
if (DateTime.Now < modUpdateStatus.WhenToRefresh) { return; }
|
|
if (!SteamManager.IsInitialized) { return; }
|
|
|
|
var installedPackages = ContentPackageManager.WorkshopPackages;
|
|
|
|
var ids = SteamManager.Workshop.GetSubscribedItemIds()
|
|
.Select(id => id.Value)
|
|
.Union(installedPackages
|
|
.Select(pkg => pkg.UgcId)
|
|
.NotNone()
|
|
.OfType<SteamWorkshopId>()
|
|
.Select(id => id.Value));
|
|
var count = ids
|
|
// Deliberately construct Steamworks.Ugc.Item directly
|
|
// to not immediately generate a Workshop data request
|
|
.Select(id => new Steamworks.Ugc.Item(id))
|
|
.Count(item =>
|
|
installedPackages.FirstOrDefault(p
|
|
=> p.UgcId.TryUnwrap(out SteamWorkshopId id) && id.Value == item.Id)
|
|
is { } pkg
|
|
// Checking that this item is downloading, waiting to be downloaded
|
|
// or is newer than the currently installed copy should be good enough,
|
|
// and should still not make a Workshop data request
|
|
&& (item.IsDownloading
|
|
|| item.IsDownloadPending
|
|
|| (item.InstallTime.TryGetValue(out var workshopInstallTime)
|
|
&& pkg.InstallTime.TryUnwrap(out var localInstallTime)
|
|
&& localInstallTime.ToUtcValue() < workshopInstallTime)));
|
|
|
|
modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
|
|
}
|
|
|
|
public override void Update(double deltaTime)
|
|
{
|
|
#if DEBUG
|
|
hostServerButton.Enabled = true;
|
|
#else
|
|
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
|
|
{
|
|
hostServerButton.Enabled = SteamManager.IsInitialized;
|
|
}
|
|
#endif
|
|
|
|
UpdateOutOfDateWorkshopItemCount();
|
|
modUpdatesButton.Visible = modUpdateStatus.Count > 0;
|
|
|
|
if (modUpdatesButton.Visible)
|
|
{
|
|
var modButtonLabelSize =
|
|
modsButton.Font.MeasureString(modsButton.Text).ToPoint()
|
|
+ new Point(GUI.IntScale(25));
|
|
modUpdatesButton.RectTransform.AbsoluteOffset =
|
|
(modButtonLabelSize.X, modsButton.Rect.Height / 2 - modUpdatesButton.Rect.Height / 2);
|
|
}
|
|
|
|
switch (selectedTab)
|
|
{
|
|
case Tab.NewGame:
|
|
campaignSetupUI.Update();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void DrawBackground(GraphicsDevice graphics, SpriteBatch spriteBatch)
|
|
{
|
|
graphics.Clear(Color.Black);
|
|
|
|
if (backgroundSprite == null)
|
|
{
|
|
#if UNSTABLE
|
|
backgroundSprite = new Sprite("Content/UnstableBackground.png", sourceRectangle: null);
|
|
#endif
|
|
if (GUIStyle.GetComponentStyle("MainMenuBackground") is { } mainMenuStyle &&
|
|
mainMenuStyle.Sprites.TryGetValue(GUIComponent.ComponentState.None, out var sprites))
|
|
{
|
|
backgroundSprite = sprites.GetRandomUnsynced()?.Sprite;
|
|
}
|
|
backgroundSprite ??= LocationType.Prefabs.GetRandomUnsynced()?.GetPortrait(0);
|
|
}
|
|
|
|
var vignette = GUIStyle.GetComponentStyle("mainmenuvignette")?.GetDefaultSprite();
|
|
float vignetteScale = Math.Min(GameMain.GraphicsWidth / vignette.size.X, GameMain.GraphicsHeight / vignette.size.Y);
|
|
|
|
Rectangle drawArea = new Rectangle(
|
|
(int)(vignette.size.X * vignetteScale / 2), 0,
|
|
(int)(GameMain.GraphicsWidth - vignette.size.X * vignetteScale / 2), GameMain.GraphicsHeight);
|
|
|
|
if (backgroundSprite?.Texture != null)
|
|
{
|
|
GUI.DrawBackgroundSprite(spriteBatch, backgroundSprite, Color.White, drawArea);
|
|
}
|
|
|
|
if (vignette != null)
|
|
{
|
|
vignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f, vignetteScale);
|
|
}
|
|
}
|
|
|
|
readonly LocalizedString[] legalCrap = new LocalizedString[]
|
|
{
|
|
TextManager.Get("privacypolicy").Fallback("Privacy policy"),
|
|
"© " + DateTime.Now.Year + " Undertow Games & FakeFish. All rights reserved.",
|
|
"© " + DateTime.Now.Year + " Daedalic Entertainment GmbH. The Daedalic logo is a trademark of Daedalic Entertainment GmbH, Germany. All rights reserved."
|
|
};
|
|
|
|
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
|
{
|
|
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
|
|
|
|
DrawBackground(graphics, spriteBatch);
|
|
|
|
GUI.Draw(Cam, spriteBatch);
|
|
|
|
if (selectedTab != Tab.Credits)
|
|
{
|
|
#if !UNSTABLE
|
|
string versionString = "Barotrauma v" + GameMain.Version + " (" + AssemblyInfo.BuildString + ", branch " + AssemblyInfo.GitBranch + ", revision " + AssemblyInfo.GitRevision + ")";
|
|
GUIStyle.SmallFont.DrawString(spriteBatch, versionString, new Vector2(HUDLayoutSettings.Padding, GameMain.GraphicsHeight - GUIStyle.SmallFont.MeasureString(versionString).Y - HUDLayoutSettings.Padding * 0.75f), Color.White * 0.7f);
|
|
#endif
|
|
LocalizedString gameAnalyticsStatus = TextManager.Get($"GameAnalyticsStatus.{GameAnalyticsManager.UserConsented}");
|
|
Vector2 textSize = GUIStyle.SmallFont.MeasureString(gameAnalyticsStatus).ToPoint().ToVector2();
|
|
GUIStyle.SmallFont.DrawString(spriteBatch, gameAnalyticsStatus, new Vector2(HUDLayoutSettings.Padding, GameMain.GraphicsHeight - GUIStyle.SmallFont.LineHeight * 2 - HUDLayoutSettings.Padding * 0.75f), Color.White * 0.7f);
|
|
|
|
|
|
Vector2 textPos = new Vector2(GameMain.GraphicsWidth - HUDLayoutSettings.Padding, GameMain.GraphicsHeight - HUDLayoutSettings.Padding * 0.75f);
|
|
for (int i = legalCrap.Length - 1; i >= 0; i--)
|
|
{
|
|
textSize = GUIStyle.SmallFont.MeasureString(legalCrap[i])
|
|
.ToPoint().ToVector2();
|
|
bool mouseOn = i == 0 &&
|
|
PlayerInput.MousePosition.X > textPos.X - textSize.X && PlayerInput.MousePosition.X < textPos.X &&
|
|
PlayerInput.MousePosition.Y > textPos.Y - textSize.Y && PlayerInput.MousePosition.Y < textPos.Y;
|
|
|
|
GUIStyle.SmallFont.DrawString(spriteBatch,
|
|
legalCrap[i], textPos - textSize,
|
|
mouseOn ? Color.White : Color.White * 0.7f);
|
|
|
|
if (i == 0)
|
|
{
|
|
GUI.DrawLine(spriteBatch, textPos, textPos - Vector2.UnitX * textSize.X, mouseOn ? Color.White : Color.White * 0.7f);
|
|
if (mouseOn && PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
|
|
{
|
|
GameMain.ShowOpenUrlInWebBrowserPrompt("http://privacypolicy.daedalic.com");
|
|
}
|
|
}
|
|
textPos.Y -= textSize.Y;
|
|
}
|
|
}
|
|
|
|
spriteBatch.End();
|
|
}
|
|
|
|
private void StartGame(SubmarineInfo selectedSub, string savePath, string mapSeed, CampaignSettings settings)
|
|
{
|
|
if (string.IsNullOrEmpty(savePath)) { return; }
|
|
|
|
var existingSaveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
|
|
if (existingSaveFiles.Any(s => s.FilePath == savePath))
|
|
{
|
|
new GUIMessageBox(TextManager.Get("SaveNameInUseHeader"), TextManager.Get("SaveNameInUseText"));
|
|
return;
|
|
}
|
|
|
|
if (selectedSub == null)
|
|
{
|
|
new GUIMessageBox(TextManager.Get("SubNotSelected"), TextManager.Get("SelectSubRequest"));
|
|
return;
|
|
}
|
|
|
|
if (!Directory.Exists(SaveUtil.TempPath))
|
|
{
|
|
Directory.CreateDirectory(SaveUtil.TempPath);
|
|
}
|
|
|
|
try
|
|
{
|
|
File.Copy(selectedSub.FilePath, Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"), true);
|
|
}
|
|
catch (System.IO.IOException e)
|
|
{
|
|
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);
|
|
GameAnalyticsManager.AddErrorEventOnce(
|
|
"MainMenuScreen.StartGame:IOException" + selectedSub.Name,
|
|
GameAnalyticsManager.ErrorSeverity.Error,
|
|
"Copying a submarine file failed. " + e.Message + "\n" + Environment.StackTrace.CleanupStackTrace());
|
|
return;
|
|
}
|
|
|
|
GameMain.LuaCs.CheckInitialize();
|
|
|
|
selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub"));
|
|
|
|
GameMain.GameSession = new GameSession(selectedSub, savePath, GameModePreset.SinglePlayerCampaign, settings, mapSeed);
|
|
GameMain.GameSession.CrewManager.CharacterInfos.Clear();
|
|
foreach (var characterInfo in campaignSetupUI.CharacterMenus.Select(m => m.CharacterInfo))
|
|
{
|
|
GameMain.GameSession.CrewManager.AddCharacterInfo(characterInfo);
|
|
}
|
|
((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel();
|
|
}
|
|
|
|
private void LoadGame(string saveFile)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(saveFile)) return;
|
|
|
|
GameMain.LuaCs.CheckInitialize();
|
|
|
|
try
|
|
{
|
|
SaveUtil.LoadGame(saveFile);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
DebugConsole.ThrowError("Loading save \"" + saveFile + "\" failed", e);
|
|
return;
|
|
}
|
|
|
|
//TODO
|
|
//GameMain.LobbyScreen.Select();
|
|
}
|
|
|
|
#region UI Methods
|
|
private void CreateCampaignSetupUI()
|
|
{
|
|
menuTabs[Tab.NewGame].ClearChildren();
|
|
menuTabs[Tab.LoadGame].ClearChildren();
|
|
|
|
var innerNewGame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[Tab.NewGame].RectTransform, Anchor.Center))
|
|
{
|
|
Stretch = true,
|
|
RelativeSpacing = 0.02f
|
|
};
|
|
var newGameContent = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.95f), innerNewGame.RectTransform, Anchor.Center),
|
|
style: "InnerFrame");
|
|
|
|
var paddedLoadGame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), menuTabs[Tab.LoadGame].RectTransform, Anchor.Center) { AbsoluteOffset = new Point(0, 10) },
|
|
style: null);
|
|
|
|
campaignSetupUI = new SinglePlayerCampaignSetupUI(newGameContent, paddedLoadGame)
|
|
{
|
|
LoadGame = LoadGame,
|
|
StartNewGame = StartGame
|
|
};
|
|
}
|
|
|
|
private void CreateHostServerFields()
|
|
{
|
|
menuTabs[Tab.HostServer].ClearChildren();
|
|
|
|
var serverSettings = XMLExtensions.TryLoadXml(ServerSettings.SettingsFile, out _)?.Root ?? new XElement("serversettings");
|
|
|
|
var name = serverSettings.GetAttributeString("name", "");
|
|
var password = serverSettings.GetAttributeString("password", "");
|
|
var isPublic = serverSettings.GetAttributeBool("IsPublic", true);
|
|
var banAfterWrongPassword = serverSettings.GetAttributeBool("banafterwrongpassword", false);
|
|
|
|
int maxPlayersElement = serverSettings.GetAttributeInt("maxplayers", 8);
|
|
if (maxPlayersElement > NetConfig.MaxPlayers)
|
|
{
|
|
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.");
|
|
}
|
|
int maxPlayers = Math.Clamp(maxPlayersElement, min: 1, max: NetConfig.MaxPlayers);
|
|
|
|
var karmaEnabled = serverSettings.GetAttributeBool("karmaenabled", true);
|
|
var selectedPlayStyle = serverSettings.GetAttributeEnum("playstyle", PlayStyle.Casual);
|
|
|
|
Vector2 textLabelSize = new Vector2(1.0f, 0.05f);
|
|
Alignment textAlignment = Alignment.CenterLeft;
|
|
Vector2 textFieldSize = new Vector2(0.5f, 1.0f);
|
|
Vector2 tickBoxSize = new Vector2(0.4f, 0.04f);
|
|
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 0.95f), menuTabs[Tab.HostServer].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
|
{
|
|
RelativeSpacing = 0.01f,
|
|
Stretch = true
|
|
};
|
|
GUIComponent parent = content;
|
|
|
|
var header = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("HostServerButton"), textAlignment: Alignment.Center, font: GUIStyle.LargeFont) { ForceUpperCase = ForceUpperCase.Yes };
|
|
header.RectTransform.IsFixedSize = true;
|
|
|
|
//play style -----------------------------------------------------
|
|
|
|
var playstyleContainer = new GUIFrame(new RectTransform(new Vector2(1.35f, 0.1f), parent.RectTransform), style: null, color: Color.Black);
|
|
|
|
playstyleBanner = new GUIImage(new RectTransform(new Vector2(1.0f, 0.1f), playstyleContainer.RectTransform),
|
|
GUIStyle.GetComponentStyle($"PlayStyleBanner.{PlayStyle.Serious}").GetSprite(GUIComponent.ComponentState.None), scaleToFit: true)
|
|
{
|
|
UserData = PlayStyle.Serious
|
|
};
|
|
float bannerAspectRatio = (float) playstyleBanner.Sprite.SourceRect.Width / playstyleBanner.Sprite.SourceRect.Height;
|
|
playstyleBanner.RectTransform.NonScaledSize = new Point(playstyleBanner.Rect.Width, (int)(playstyleBanner.Rect.Width / bannerAspectRatio));
|
|
playstyleBanner.RectTransform.IsFixedSize = true;
|
|
new GUIFrame(new RectTransform(Vector2.One, playstyleBanner.RectTransform), "InnerGlow", color: Color.Black);
|
|
|
|
new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.05f), playstyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.03f) },
|
|
"playstyle name goes here", font: GUIStyle.SmallFont, textAlignment: Alignment.Center, textColor: Color.White, style: "GUISlopedHeader");
|
|
|
|
new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform, Anchor.CenterLeft)
|
|
{ RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
|
|
style: "UIToggleButton")
|
|
{
|
|
OnClicked = (btn, userdata) =>
|
|
{
|
|
int playStyleIndex = (int)playstyleBanner.UserData - 1;
|
|
if (playStyleIndex < 0) { playStyleIndex = Enum.GetValues(typeof(PlayStyle)).Length - 1; }
|
|
SetServerPlayStyle((PlayStyle)playStyleIndex);
|
|
return true;
|
|
}
|
|
}.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally);
|
|
|
|
new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), playstyleContainer.RectTransform, Anchor.CenterRight)
|
|
{ RelativeOffset = new Vector2(0.02f, 0.0f), MaxSize = new Point(int.MaxValue, (int)(150 * GUI.Scale)) },
|
|
style: "UIToggleButton")
|
|
{
|
|
OnClicked = (btn, userdata) =>
|
|
{
|
|
int playStyleIndex = (int)playstyleBanner.UserData + 1;
|
|
if (playStyleIndex >= Enum.GetValues(typeof(PlayStyle)).Length) { playStyleIndex = 0; }
|
|
SetServerPlayStyle((PlayStyle)playStyleIndex);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
LocalizedString longestPlayStyleStr = "";
|
|
foreach (PlayStyle playStyle in Enum.GetValues(typeof(PlayStyle)))
|
|
{
|
|
LocalizedString playStyleStr = TextManager.Get("servertagdescription." + playStyle);
|
|
if (playStyleStr.Length > longestPlayStyleStr.Length) { longestPlayStyleStr = playStyleStr; }
|
|
}
|
|
|
|
playstyleDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), playstyleContainer.RectTransform, Anchor.BottomCenter),
|
|
longestPlayStyleStr, style: null, wrap: true)
|
|
{
|
|
Color = Color.Black * 0.8f,
|
|
TextColor = GUIStyle.GetComponentStyle("GUITextBlock").TextColor
|
|
};
|
|
playstyleDescription.Padding = Vector4.One * 10.0f * GUI.Scale;
|
|
playstyleDescription.CalculateHeightFromText(padding: (int)(15 * GUI.Scale));
|
|
playstyleDescription.RectTransform.NonScaledSize = new Point(playstyleDescription.Rect.Width, playstyleDescription.Rect.Height);
|
|
playstyleDescription.RectTransform.IsFixedSize = true;
|
|
playstyleContainer.RectTransform.NonScaledSize = new Point(playstyleContainer.Rect.Width, playstyleBanner.Rect.Height + playstyleDescription.Rect.Height);
|
|
playstyleContainer.RectTransform.IsFixedSize = true;
|
|
|
|
SetServerPlayStyle(selectedPlayStyle);
|
|
|
|
//other settings -----------------------------------------------------
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.025f), content.RectTransform), style: null);
|
|
|
|
var label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("ServerName"), textAlignment: textAlignment);
|
|
serverNameBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), text: name, textAlignment: textAlignment)
|
|
{
|
|
MaxTextLength = NetConfig.ServerNameMaxLength,
|
|
OverflowClip = true
|
|
};
|
|
label.RectTransform.IsFixedSize = true;
|
|
|
|
var maxPlayersLabel = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("MaxPlayers"), textAlignment: textAlignment);
|
|
var buttonContainer = new GUILayoutGroup(new RectTransform(textFieldSize, maxPlayersLabel.RectTransform, Anchor.CenterRight), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
|
{
|
|
Stretch = true,
|
|
RelativeSpacing = 0.1f
|
|
};
|
|
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIMinusButton", textAlignment: Alignment.Center)
|
|
{
|
|
UserData = -1,
|
|
OnClicked = ChangeMaxPlayers,
|
|
ClickSound = GUISoundType.Decrease
|
|
};
|
|
maxPlayersBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center)
|
|
{
|
|
Text = maxPlayers.ToString()
|
|
};
|
|
maxPlayersBox.OnEnterPressed += (GUITextBox sender, string text) =>
|
|
{
|
|
maxPlayersBox.Deselect();
|
|
return true;
|
|
};
|
|
maxPlayersBox.OnDeselected += (GUITextBox sender, Microsoft.Xna.Framework.Input.Keys key) =>
|
|
{
|
|
int.TryParse(maxPlayersBox.Text, out int currMaxPlayers);
|
|
currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers, 1, NetConfig.MaxPlayers);
|
|
maxPlayersBox.Text = currMaxPlayers.ToString();
|
|
};
|
|
new GUIButton(new RectTransform(Vector2.One, buttonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center)
|
|
{
|
|
UserData = 1,
|
|
OnClicked = ChangeMaxPlayers,
|
|
ClickSound = GUISoundType.Increase
|
|
};
|
|
maxPlayersLabel.RectTransform.IsFixedSize = true;
|
|
|
|
label = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform), TextManager.Get("Password"), textAlignment: textAlignment);
|
|
passwordBox = new GUITextBox(new RectTransform(textFieldSize, label.RectTransform, Anchor.CenterRight), text: password, textAlignment: textAlignment)
|
|
{
|
|
Censor = true
|
|
};
|
|
label.RectTransform.IsFixedSize = true;
|
|
|
|
var languageLabel = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform),
|
|
TextManager.Get("Language"), textAlignment: textAlignment);
|
|
languageDropdown = new GUIDropDown(new RectTransform(textFieldSize, languageLabel.RectTransform, Anchor.CenterRight));
|
|
foreach (var language in ServerLanguageOptions.Options)
|
|
{
|
|
languageDropdown.AddItem(language.Label, language.Identifier);
|
|
}
|
|
var defaultLanguage = ServerLanguageOptions.PickLanguage(GameSettings.CurrentConfig.Language);
|
|
var settingsLanguage = serverSettings.GetAttributeIdentifier("language", defaultLanguage.Value).ToLanguageIdentifier();
|
|
if (!ServerLanguageOptions.Options.Any(o => o.Identifier == settingsLanguage))
|
|
{
|
|
settingsLanguage = defaultLanguage;
|
|
}
|
|
languageDropdown.Select(ServerLanguageOptions.Options.FindIndex(o => o.Identifier == settingsLanguage));
|
|
|
|
var serverExecutableLabel = new GUITextBlock(new RectTransform(textLabelSize, parent.RectTransform),
|
|
TextManager.Get("ServerExecutable"), textAlignment: textAlignment);
|
|
const string vanillaServerOption = "Vanilla";
|
|
serverExecutableDropdown
|
|
= new GUIDropDown(new RectTransform(textFieldSize, serverExecutableLabel.RectTransform, Anchor.CenterRight),
|
|
vanillaServerOption);
|
|
var listBoxSize = serverExecutableDropdown.ListBox.RectTransform.RelativeSize;
|
|
serverExecutableDropdown.ListBox.RectTransform.RelativeSize = new Vector2(listBoxSize.X * 1.5f, listBoxSize.Y);
|
|
serverExecutableDropdown.AddItem(vanillaServerOption, userData: null);
|
|
serverExecutableDropdown.OnSelected = (selected, userData) =>
|
|
{
|
|
if (userData != null)
|
|
{
|
|
var warningBox = new GUIMessageBox(headerText: TextManager.Get("Warning"),
|
|
text: TextManager.GetWithVariable("ModServerExesAtYourOwnRisk", "[exename]", serverExecutableDropdown.Text),
|
|
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
|
warningBox.Buttons[0].OnClicked = (_, __) =>
|
|
{
|
|
warningBox.Close();
|
|
return false;
|
|
};
|
|
warningBox.Buttons[1].OnClicked = (_, __) =>
|
|
{
|
|
serverExecutableDropdown.Select(0);
|
|
warningBox.Close();
|
|
return false;
|
|
};
|
|
}
|
|
|
|
serverExecutableDropdown.Text = ToolBox.LimitString(serverExecutableDropdown.Text,
|
|
serverExecutableDropdown.Font, serverExecutableDropdown.Rect.Width * 8 / 10);
|
|
|
|
return true;
|
|
};
|
|
serverExecutableLabel.RectTransform.IsFixedSize = true;
|
|
|
|
// tickbox upper ---------------
|
|
|
|
var tickboxAreaUpper = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal: true);
|
|
|
|
isPublicBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get("PublicServer"))
|
|
{
|
|
Selected = isPublic,
|
|
ToolTip = TextManager.Get("PublicServerToolTip")
|
|
};
|
|
|
|
wrongPasswordBanBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaUpper.RectTransform), TextManager.Get("ServerSettingsBanAfterWrongPassword"))
|
|
{
|
|
Selected = banAfterWrongPassword
|
|
};
|
|
|
|
tickboxAreaUpper.RectTransform.IsFixedSize = true;
|
|
|
|
// tickbox lower ---------------
|
|
|
|
var tickboxAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, tickBoxSize.Y), parent.RectTransform), isHorizontal: true);
|
|
|
|
karmaBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickboxAreaLower.RectTransform), TextManager.Get("HostServerKarmaSetting"))
|
|
{
|
|
Selected = !karmaEnabled,
|
|
ToolTip = TextManager.Get("hostserverkarmasettingtooltip")
|
|
};
|
|
|
|
tickboxAreaLower.RectTransform.IsFixedSize = true;
|
|
|
|
//spacing
|
|
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), content.RectTransform), style: null);
|
|
|
|
new GUIButton(new RectTransform(new Vector2(0.4f, 0.07f), content.RectTransform), TextManager.Get("StartServerButton"), style: "GUIButtonLarge")
|
|
{
|
|
OnClicked = (btn, userdata) =>
|
|
{
|
|
CheckServerName();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
void CheckServerName()
|
|
{
|
|
string name = serverNameBox.Text;
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
serverNameBox.Flash();
|
|
return;
|
|
}
|
|
if (isPublicBox.Selected && ForbiddenWordFilter.IsForbidden(name, out string forbiddenWord))
|
|
{
|
|
var msgBox = new GUIMessageBox("",
|
|
TextManager.GetWithVariables("forbiddenservernameverification", ("[forbiddenword]", forbiddenWord), ("[servername]", name)),
|
|
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
|
|
msgBox.Buttons[0].OnClicked += (_, __) =>
|
|
{
|
|
CheckServerExe();
|
|
msgBox.Close();
|
|
return true;
|
|
};
|
|
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
|
return;
|
|
}
|
|
CheckServerExe();
|
|
}
|
|
|
|
void CheckServerExe()
|
|
{
|
|
if (serverExecutableDropdown?.SelectedData is ServerExecutableFile serverExe &&
|
|
serverExe.ContentPackage.GameVersion < GameMain.VanillaContent.GameVersion)
|
|
{
|
|
var msgBox = new GUIMessageBox(string.Empty,
|
|
TextManager.GetWithVariables("versionmismatchwarning",
|
|
("[gameversion]", serverExe.ContentPackage.GameVersion.ToString()),
|
|
("[contentversion]", GameMain.VanillaContent.GameVersion.ToString())) + "\n\n"+
|
|
TextManager.GetWithVariable("versionmismatch.verifylaunch", "[exename]", serverExe.ContentPackage.Name),
|
|
new LocalizedString[] { TextManager.Get("yes"), TextManager.Get("no") });
|
|
msgBox.Buttons[0].OnClicked += (_, __) =>
|
|
{
|
|
TryStartServer();
|
|
msgBox.Close();
|
|
return true;
|
|
};
|
|
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
|
return;
|
|
}
|
|
TryStartServer();
|
|
}
|
|
}
|
|
|
|
private void SetServerPlayStyle(PlayStyle playStyle)
|
|
{
|
|
playstyleBanner.Sprite = GUIStyle
|
|
.GetComponentStyle($"PlayStyleBanner.{playStyle}")
|
|
.GetSprite(GUIComponent.ComponentState.None);
|
|
playstyleBanner.UserData = playStyle;
|
|
|
|
var nameText = playstyleBanner.GetChild<GUITextBlock>();
|
|
nameText.Text = TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag." + playStyle));
|
|
nameText.Color = playstyleBanner.Sprite
|
|
.SourceElement.GetAttributeColor("BannerColor") ?? Color.White;
|
|
nameText.RectTransform.NonScaledSize = (nameText.Font.MeasureString(nameText.Text) + new Vector2(25, 10) * GUI.Scale).ToPoint();
|
|
|
|
playstyleDescription.Text = TextManager.Get("servertagdescription." + playStyle);
|
|
playstyleDescription.TextAlignment = playstyleDescription.WrappedText.Contains('\n') ?
|
|
Alignment.CenterLeft : Alignment.Center;
|
|
}
|
|
#endregion
|
|
|
|
private void FetchRemoteContent()
|
|
{
|
|
string remoteContentUrl = GameSettings.CurrentConfig.RemoteMainMenuContentUrl;
|
|
if (string.IsNullOrEmpty(remoteContentUrl)) { return; }
|
|
try
|
|
{
|
|
var client = new RestClient(remoteContentUrl);
|
|
var request = new RestRequest("MenuContent.xml", Method.GET);
|
|
TaskPool.Add("RequestMainMenuRemoteContent", client.ExecuteAsync(request),
|
|
RemoteContentReceived);
|
|
}
|
|
|
|
catch (Exception e)
|
|
{
|
|
#if DEBUG
|
|
DebugConsole.ThrowError("Fetching remote content to the main menu failed.", e);
|
|
#endif
|
|
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.FetchRemoteContent:Exception", GameAnalyticsManager.ErrorSeverity.Error,
|
|
"Fetching remote content to the main menu failed. " + e.Message);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void RemoteContentReceived(Task t)
|
|
{
|
|
try
|
|
{
|
|
if (!t.TryGetResult(out IRestResponse remoteContentResponse)) { throw new Exception("Task did not return a valid result"); }
|
|
if (remoteContentResponse.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
DebugConsole.AddWarning(
|
|
"Failed to receive remote main menu content. " +
|
|
"There may be an issue with your internet connection, or the master server might be temporarily unavailable " +
|
|
$"(error code: {remoteContentResponse.StatusCode})");
|
|
return;
|
|
}
|
|
string xml = remoteContentResponse.Content;
|
|
int index = xml.IndexOf('<');
|
|
if (index > 0) { xml = xml.Substring(index, xml.Length - index); }
|
|
if (!string.IsNullOrWhiteSpace(xml))
|
|
{
|
|
remoteContentDoc = XDocument.Parse(xml);
|
|
foreach (var subElement in remoteContentDoc?.Root.Elements())
|
|
{
|
|
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
|
|
}
|
|
}
|
|
}
|
|
|
|
catch (Exception e)
|
|
{
|
|
#if DEBUG
|
|
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
|
|
#endif
|
|
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.RemoteContentReceived:Exception", GameAnalyticsManager.ErrorSeverity.Error,
|
|
"Reading received remote main menu content failed. " + e.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|