using Barotrauma.Extensions; using Barotrauma.Networking; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Linq; namespace Barotrauma { partial class NetLobbyScreen : Screen { private GUIFrame infoFrame, chatFrame, playerListFrame; private GUIFrame myCharacterFrame; private GUIListBox playerList; private GUIListBox subList, modeList, chatBox; public GUIListBox ChatBox { get { return chatBox; } } private GUIScrollBar levelDifficultyScrollBar; private GUIButton[] traitorProbabilityButtons; private GUITextBlock traitorProbabilityText; private GUIButton[] botCountButtons; private GUITextBlock botCountText; private GUIButton[] botSpawnModeButtons; private GUITextBlock botSpawnModeText; private GUIButton[] missionTypeButtons; private GUIComponent missionTypeContainer; private GUIListBox jobList; private GUITextBox textBox, seedBox; public GUITextBox TextBox { get { return textBox; } } private GUIFrame defaultModeContainer, campaignContainer; private GUIButton campaignViewButton, spectateButton, settingsButton; private GUITickBox playYourself; private GUIFrame playerInfoContainer; private GUIButton jobInfoFrame; private GUIButton playerFrame; private GUITickBox autoRestartBox; private GUIDropDown shuttleList; private GUITickBox shuttleTickBox; private CampaignUI campaignUI; private GUIComponent campaignSetupUI; private Sprite backgroundSprite; private GUITextBox serverMessage; private GUIButton faceSelectionLeft; private GUIButton faceSelectionRight; private float autoRestartTimer; //persistent characterinfo provided by the server //(character settings cannot be edited when this is set) private CharacterInfo campaignCharacterInfo; public bool CampaignCharacterDiscarded { get; private set; } //elements that can only be used by the host private List clientDisabledElements = new List(); //elements that aren't shown client-side private List clientHiddenElements = new List(); private bool AllowSubSelection { get { return GameMain.Server != null || GameMain.NetworkMember.Voting.AllowSubVoting || (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectSub)); } } public GUITextBox ServerMessage { get { return serverMessage; } } public string ServerMessageText { get { return serverMessage.Text; } set { serverMessage.Text = value; } } public GUIButton ShowLogButton { get; private set; } public GUIListBox SubList { get { return subList; } } public GUIDropDown ShuttleList { get { return shuttleList; } } public GUITickBox ShuttleTickBox { get { return shuttleTickBox; } } public GUIListBox ModeList { get { return modeList; } } public int SelectedModeIndex { get { return modeList.SelectedIndex; } set { modeList.Select(value); } } public GUIListBox PlayerList { get { return playerList; } } public GUIButton StartButton { get; private set; } public bool StartButtonEnabled { get { return StartButton.Enabled; } set { StartButton.Enabled = value; } } public GUITickBox ReadyToStartBox { get; private set; } public GUIFrame MyCharacterFrame { get { return myCharacterFrame; } } public bool MyCharacterFrameOpen; public GUIFrame InfoFrame { get { return infoFrame; } } public Submarine SelectedSub { get { return subList.SelectedData as Submarine; } set { subList.Select(value); } } public Submarine SelectedShuttle { get { return shuttleList.SelectedData as Submarine; } } public bool UsingShuttle { get { return shuttleTickBox.Selected; } set { shuttleTickBox.Selected = value; if (GameMain.Client != null) shuttleTickBox.Enabled = false; } } public GameModePreset SelectedMode { get { return modeList.SelectedData as GameModePreset; } } public int MissionTypeIndex { get { return (int)missionTypeContainer.UserData; } set { missionTypeContainer.UserData = value; } } public List JobPreferences { get { List jobPreferences = new List(); foreach (GUIComponent child in jobList.Content.Children) { JobPrefab jobPrefab = child.UserData as JobPrefab; if (jobPrefab == null) continue; jobPreferences.Add(jobPrefab); } return jobPreferences; } } public string LevelSeed { get { return levelSeed; } set { if (levelSeed == value) return; levelSeed = value; backgroundSprite = LocationType.Random(levelSeed)?.GetPortrait(ToolBox.StringToInt(levelSeed)); seedBox.Text = levelSeed; lastUpdateID++; } } public string AutoRestartText() { if (GameMain.Server != null) { if (!GameMain.Server.AutoRestart || GameMain.Server.ConnectedClients.Count == 0) return ""; return TextManager.Get("RestartingIn") + " " + ToolBox.SecondsToReadableTime(Math.Max(GameMain.Server.AutoRestartTimer, 0)); } if (autoRestartTimer == 0.0f) return ""; return TextManager.Get("RestartingIn") + " " + ToolBox.SecondsToReadableTime(Math.Max(autoRestartTimer, 0)); } public NetLobbyScreen() { defaultModeContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), Frame.RectTransform, Anchor.Center), style: null); campaignContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.75f), Frame.RectTransform, Anchor.TopCenter), style: null) { Visible = false }; float panelSpacing = 0.02f; //server info panel ------------------------------------------------------------ infoFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.65f), defaultModeContainer.RectTransform)); var infoFrameContent = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), infoFrame.RectTransform, Anchor.Center), style: null); //chatbox ---------------------------------------------------------------------- chatFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.35f - panelSpacing), defaultModeContainer.RectTransform, Anchor.BottomLeft)); GUIFrame paddedChatFrame = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.85f), chatFrame.RectTransform, Anchor.Center), style: null); chatBox = new GUIListBox(new RectTransform(new Point(paddedChatFrame.Rect.Width, paddedChatFrame.Rect.Height - 30), paddedChatFrame.RectTransform) { IsFixedSize = false }); textBox = new GUITextBox(new RectTransform(new Point(paddedChatFrame.Rect.Width, 20), paddedChatFrame.RectTransform, Anchor.BottomLeft) { IsFixedSize = false }) { MaxTextLength = ChatMessage.MaxLength, Font = GUI.SmallFont }; textBox.OnEnterPressed = (tb, userdata) => { GameMain.NetworkMember?.EnterChatMessage(tb, userdata); return true; }; textBox.OnTextChanged += (tb, userdata) => { GameMain.NetworkMember?.TypingChatMessage(tb, userdata); return true; }; //player info panel ------------------------------------------------------------ myCharacterFrame = new GUIFrame(new RectTransform(new Vector2(0.3f - panelSpacing, 0.65f), defaultModeContainer.RectTransform, Anchor.TopRight)); playerInfoContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), myCharacterFrame.RectTransform, Anchor.Center), style: null); playYourself = new GUITickBox(new RectTransform(new Vector2(0.06f, 0.06f), myCharacterFrame.RectTransform) { RelativeOffset = new Vector2(0.05f,0.05f) }, TextManager.Get("PlayYourself")) { OnSelected = (tickBox) => { SetPlayYourself(tickBox.Selected); return true; }, UserData = "playyourself" }; var toggleMyPlayerFrame = new GUIButton(new RectTransform(new Point(25, 70), myCharacterFrame.RectTransform, Anchor.TopLeft, Pivot.TopRight), "", style: "GUIButtonHorizontalArrow"); toggleMyPlayerFrame.OnClicked += (GUIButton btn, object userdata) => { MyCharacterFrameOpen = !MyCharacterFrameOpen; foreach (GUIComponent child in btn.Children) { child.SpriteEffects = MyCharacterFrameOpen ? SpriteEffects.FlipHorizontally : SpriteEffects.None; } return true; }; //player list ------------------------------------------------------------------ playerListFrame = new GUIFrame(new RectTransform(new Vector2(0.3f - panelSpacing, 0.35f - panelSpacing), defaultModeContainer.RectTransform, Anchor.BottomRight)); GUIFrame paddedPlayerListFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.85f), playerListFrame.RectTransform, Anchor.Center), style: null); playerList = new GUIListBox(new RectTransform(Vector2.One, paddedPlayerListFrame.RectTransform)) { OnSelected = SelectPlayer }; //-------------------------------------------------------------------------------------------------------------------------------- //infoframe contents //-------------------------------------------------------------------------------------------------------------------------------- var infoColumnContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.7f - 0.02f, 0.75f), infoFrameContent.RectTransform, Anchor.BottomLeft), isHorizontal: true, childAnchor: Anchor.BottomLeft) { RelativeSpacing = 0.02f, Stretch = true }; var leftInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.35f, 1.0f), infoColumnContainer.RectTransform, Anchor.BottomLeft)) { RelativeSpacing = 0.02f, Stretch = true }; var midInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.35f, 1.0f), infoColumnContainer.RectTransform, Anchor.BottomLeft)) { RelativeSpacing = 0.02f, Stretch = true }; var rightInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.85f), infoFrameContent.RectTransform, Anchor.TopRight)) { RelativeSpacing = 0.02f, Stretch = true }; var topButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), rightInfoColumn.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight) { RelativeSpacing = 0.05f, Stretch = true }; //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), rightInfoColumn.RectTransform), style: null); //server info ------------------------------------------------------------------ var serverName = new GUITextBox(new RectTransform(new Vector2(0.3f, 0.05f), infoFrameContent.RectTransform)) { TextGetter = GetServerName, Enabled = GameMain.Server != null, }; serverName.OnTextChanged += ChangeServerName; clientDisabledElements.Add(serverName); serverMessage = new GUITextBox(new RectTransform(new Vector2(infoColumnContainer.RectTransform.RelativeSize.X, 0.15f), infoFrameContent.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.07f) }) { Wrap = true }; serverMessage.OnTextChanged += UpdateServerMessage; clientDisabledElements.Add(serverMessage); settingsButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), topButtonContainer.RectTransform, Anchor.TopRight), TextManager.Get("ServerSettingsButton")); clientHiddenElements.Add(settingsButton); ShowLogButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), topButtonContainer.RectTransform, Anchor.TopRight), TextManager.Get("ServerLog")) { OnClicked = (GUIButton button, object userData) => { if (GameMain.NetworkMember.ServerLog.LogFrame == null) { GameMain.NetworkMember.ServerLog.CreateLogFrame(); } else { GameMain.NetworkMember.ServerLog.LogFrame = null; GUI.KeyboardDispatcher.Subscriber = null; } return true; } }; clientHiddenElements.Add(ShowLogButton); //submarine list ------------------------------------------------------------------ var subLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftInfoColumn.RectTransform), TextManager.Get("Submarine")); subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.9f), leftInfoColumn.RectTransform)) { OnSelected = VotableClicked }; var voteText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), subLabel.RectTransform, Anchor.TopRight), TextManager.Get("Votes"), textAlignment: Alignment.CenterRight) { UserData = "subvotes", Visible = false }; //respawn shuttle ------------------------------------------------------------------ shuttleTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), TextManager.Get("RespawnShuttle")) { Selected = true, OnSelected = (GUITickBox box) => { shuttleList.Enabled = box.Selected; if (GameMain.Server != null) lastUpdateID++; return true; } }; shuttleList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), elementCount: 10); //gamemode ------------------------------------------------------------------ var modeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), TextManager.Get("GameMode")); modeList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), midInfoColumn.RectTransform)) { OnSelected = VotableClicked }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), "Coming up: Multiplayer campaign", textAlignment: Alignment.Center, style: "ListBoxElement") { ToolTip = "Multiplayer campaign mode is not available in this version of Barotrauma. Coming up in the Early Access release!" }; voteText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), modeLabel.RectTransform, Anchor.TopRight), TextManager.Get("Votes"), textAlignment: Alignment.CenterRight) { UserData = "modevotes", Visible = false }; foreach (GameModePreset mode in GameModePreset.List) { if (mode.IsSinglePlayer) continue; GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), modeList.Content.RectTransform), mode.Name, style: "ListBoxElement", textAlignment: Alignment.CenterLeft) { ToolTip = mode.Description, UserData = mode }; } //mission type ------------------------------------------------------------------ missionTypeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), isHorizontal: true) { UserData = 0, Visible = false, Stretch = true }; var missionTypeText = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1.0f), missionTypeContainer.RectTransform), TextManager.Get("MissionType")); missionTypeButtons = new GUIButton[2]; missionTypeButtons[0] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), missionTypeContainer.RectTransform), "<") { UserData = -1 }; new GUITextBlock(new RectTransform(new Vector2(0.4f, 1.0f), missionTypeContainer.RectTransform), TextManager.Get("MissionType.Random"), textAlignment: Alignment.Center) { UserData = 0 }; missionTypeButtons[1] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), missionTypeContainer.RectTransform), ">") { UserData = 1 }; clientDisabledElements.AddRange(missionTypeButtons); //seed ------------------------------------------------------------------ new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("LevelSeed")); seedBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform)); seedBox.OnTextChanged += SelectSeed; clientDisabledElements.Add(seedBox); LevelSeed = ToolBox.RandomSeed(8); //level difficulty ------------------------------------------------------------------ new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("LevelDifficulty")); levelDifficultyScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), barSize: 0.1f); clientDisabledElements.Add(levelDifficultyScrollBar); //traitor probability ------------------------------------------------------------------ new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), rightInfoColumn.RectTransform), style: null); //spacing new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("Traitors")); var traitorProbContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), isHorizontal: true); traitorProbabilityButtons = new GUIButton[2]; traitorProbabilityButtons[0] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), traitorProbContainer.RectTransform), "<") { UserData = -1 }; traitorProbabilityText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), traitorProbContainer.RectTransform), TextManager.Get("No"), textAlignment: Alignment.Center); traitorProbabilityButtons[1] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), traitorProbContainer.RectTransform), ">") { UserData = 1 }; clientDisabledElements.AddRange(traitorProbabilityButtons); //bot count ------------------------------------------------------------------ new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("BotCount")); var botCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), isHorizontal: true); botCountButtons = new GUIButton[2]; botCountButtons[0] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), botCountContainer.RectTransform), "<") { UserData = -1 }; botCountText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), botCountContainer.RectTransform), "0", textAlignment: Alignment.Center); botCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), botCountContainer.RectTransform), ">") { UserData = 1 }; clientDisabledElements.AddRange(botCountButtons); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("BotSpawnMode")); var botSpawnModeContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), isHorizontal: true); botSpawnModeButtons = new GUIButton[2]; botSpawnModeButtons[0] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), botSpawnModeContainer.RectTransform), "<") { UserData = -1 }; botSpawnModeText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), botSpawnModeContainer.RectTransform), "", textAlignment: Alignment.Center); botSpawnModeButtons[1] = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), botSpawnModeContainer.RectTransform), ">") { UserData = 1 }; clientDisabledElements.AddRange(botSpawnModeButtons); //misc buttons ------------------------------------------------------------------ new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), rightInfoColumn.RectTransform), style: null); //spacing autoRestartBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), TextManager.Get("AutoRestart")) { OnSelected = ToggleAutoRestart }; clientDisabledElements.Add(autoRestartBox); var restartText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightInfoColumn.RectTransform), "", font: GUI.SmallFont) { TextGetter = AutoRestartText }; StartButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight), TextManager.Get("StartGameButton"), style: "GUIButtonLarge"); clientHiddenElements.Add(StartButton); ReadyToStartBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.06f), infoFrameContent.RectTransform, Anchor.BottomRight), TextManager.Get("ReadyToStartTickBox"), GUI.SmallFont) { Visible = false }; campaignViewButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.06f) }, TextManager.Get("CampaignView"), style: "GUIButtonLarge") { OnClicked = (btn, obj) => { ToggleCampaignView(true); return true; }, Visible = false }; spectateButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight), TextManager.Get("SpectateButton"), style: "GUIButtonLarge"); } public override void Deselect() { textBox.Deselect(); myCharacterFrame.GetChild().Visible = true; CampaignCharacterDiscarded = false; } public override void Select() { if (GameMain.NetworkMember == null) return; Character.Controlled = null; GameMain.LightManager.LosEnabled = false; CampaignCharacterDiscarded = false; textBox.Select(); myCharacterFrame.RectTransform.AbsoluteOffset = new Point(0, 0); myCharacterFrame.GetChild().Visible = false; subList.Enabled = AllowSubSelection || GameMain.Server != null; shuttleList.Enabled = AllowSubSelection || GameMain.Server != null; modeList.Enabled = GameMain.Server != null || GameMain.NetworkMember.Voting.AllowModeVoting || (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.SelectMode)); ServerName = (GameMain.Server == null) ? ServerName : GameMain.Server.Name; //disable/hide elements the clients are not supposed to use/see clientDisabledElements.ForEach(c => c.Enabled = GameMain.Server != null); clientHiddenElements.ForEach(c => c.Visible = GameMain.Server != null); ShowLogButton.Visible = GameMain.Server != null || (GameMain.Client != null && GameMain.Client.HasPermission(ClientPermissions.ServerLog)); if (GameMain.Client != null) { spectateButton.Visible = GameMain.Client.GameStarted; ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Selected = false; GameMain.Client.SetReadyToStart(ReadyToStartBox); } else { spectateButton.Visible = false; ReadyToStartBox.Visible = false; } SetPlayYourself(playYourself.Selected); if (IsServer && GameMain.Server != null) { List subsToShow = Submarine.SavedSubmarines.Where(s => !s.HasTag(SubmarineTag.HideInMenus)).ToList(); ReadyToStartBox.Visible = false; StartButton.OnClicked = GameMain.Server.StartGameClicked; settingsButton.OnClicked = GameMain.Server.ToggleSettingsFrame; int prevSelectedSub = subList.SelectedIndex; UpdateSubList(subList, subsToShow); int prevSelectedShuttle = shuttleList.SelectedIndex; UpdateSubList(shuttleList, subsToShow); modeList.OnSelected = VotableClicked; modeList.OnSelected = SelectMode; subList.OnSelected = VotableClicked; subList.OnSelected = SelectSub; shuttleList.OnSelected = SelectSub; levelDifficultyScrollBar.OnMoved = (GUIScrollBar scrollBar, float barScroll) => { SetLevelDifficulty(barScroll * 100.0f); return true; }; traitorProbabilityButtons[0].OnClicked = traitorProbabilityButtons[1].OnClicked = ToggleTraitorsEnabled; botCountButtons[0].OnClicked = botCountButtons[1].OnClicked = ChangeBotCount; botSpawnModeButtons[0].OnClicked = botSpawnModeButtons[1].OnClicked = ChangeBotSpawnMode; missionTypeButtons[0].OnClicked = missionTypeButtons[1].OnClicked = ToggleMissionType; if (subList.SelectedComponent == null) subList.Select(Math.Max(0, prevSelectedSub)); if (shuttleList.Selected == null) { var shuttles = shuttleList.GetChildren().Where(c => c.UserData is Submarine && ((Submarine)c.UserData).HasTag(SubmarineTag.Shuttle)); if (prevSelectedShuttle == -1 && shuttles.Any()) { shuttleList.SelectItem(shuttles.First().UserData); } else { shuttleList.Select(Math.Max(0, prevSelectedShuttle)); } } GameAnalyticsManager.SetCustomDimension01("multiplayer"); if (GameModePreset.List.Count > 0 && modeList.SelectedComponent == null) modeList.Select(0); GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients); } else if (GameMain.Client != null) { GameMain.Client.Voting.ResetVotes(GameMain.Client.ConnectedClients); SetPlayYourself(true); spectateButton.OnClicked = GameMain.Client.SpectateClicked; ReadyToStartBox.OnSelected = GameMain.Client.SetReadyToStart; } GameMain.NetworkMember.EndVoteCount = 0; GameMain.NetworkMember.EndVoteMax = 1; base.Select(); } public void RandomizeSettings() { if (GameMain.Server == null) return; if (GameMain.Server.RandomizeSeed) LevelSeed = ToolBox.RandomSeed(8); if (GameMain.Server.SubSelectionMode == SelectionMode.Random) { var nonShuttles = subList.Content.Children.Where(c => c.UserData is Submarine && !((Submarine)c.UserData).HasTag(SubmarineTag.Shuttle)); subList.Select(nonShuttles.GetRandom()); } if (GameMain.Server.ModeSelectionMode == SelectionMode.Random) { var allowedGameModes = GameModePreset.List.FindAll(m => !m.IsSinglePlayer && m.Identifier != "multiplayercampaign"); modeList.Select(allowedGameModes[Rand.Range(0, allowedGameModes.Count)]); } } public void ShowSpectateButton() { if (GameMain.Client == null) return; spectateButton.Visible = true; spectateButton.Enabled = true; } public void SetCampaignCharacterInfo(CharacterInfo characterInfo) { if (CampaignCharacterDiscarded) return; campaignCharacterInfo = characterInfo; if (campaignCharacterInfo != null) { UpdatePlayerFrame(campaignCharacterInfo, false); } else { UpdatePlayerFrame(null, true); } } private void UpdatePlayerFrame(CharacterInfo characterInfo, bool allowEditing = true) { if (!playYourself.Selected) return; if (characterInfo == null) { characterInfo = new CharacterInfo(Character.HumanConfigFile, GameMain.NetworkMember.Name, GameMain.Config.CharacterGender, null) { Race = GameMain.Config.CharacterRace, HairIndex = GameMain.Config.CharacterHairIndex, BeardIndex = GameMain.Config.CharacterBeardIndex, MoustacheIndex = GameMain.Config.CharacterMoustacheIndex, FaceAttachmentIndex = GameMain.Config.CharacterFaceAttachmentIndex, }; characterInfo.Head.HeadSpriteId = GameMain.Config.CharacterHeadIndex; characterInfo.LoadHeadAttachments(); GameMain.NetworkMember.CharacterInfo = characterInfo; } playerInfoContainer.ClearChildren(); GUIComponent infoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), playerInfoContainer.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter) { Stretch = true }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), infoContainer.RectTransform), characterInfo.Name, font: GUI.LargeFont, textAlignment: Alignment.Center, wrap: true); GUIComponent headContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 0.2f), infoContainer.RectTransform, Anchor.TopCenter), isHorizontal: true) { Stretch = true }; if (allowEditing) { faceSelectionLeft = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), headContainer.RectTransform), "", style: "GUIButtonHorizontalArrow") { Enabled = generatedHeads.UndoCount > 1, UserData = -1, OnClicked = SwitchHead }; faceSelectionLeft.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally); } new GUICustomComponent(new RectTransform(new Vector2(0.3f, 1.0f), headContainer.RectTransform), onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2())); if (allowEditing) { faceSelectionRight = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), headContainer.RectTransform), style: "GUIButtonHorizontalArrow") { UserData = 1, OnClicked = SwitchHead }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), infoContainer.RectTransform), TextManager.Get("Gender"), textAlignment: Alignment.Center); GUIComponent genderContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.06f), infoContainer.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; GUIButton maleButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), genderContainer.RectTransform), TextManager.Get("Male")) { UserData = Gender.Male, OnClicked = SwitchGender }; GUIButton femaleButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), genderContainer.RectTransform), TextManager.Get("Female")) { UserData = Gender.Female, OnClicked = SwitchGender }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), infoContainer.RectTransform), TextManager.Get("JobPreferences")); jobList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), infoContainer.RectTransform)) { Enabled = false }; int i = 1; foreach (string jobIdentifier in GameMain.Config.JobPreferences) { JobPrefab job = JobPrefab.List.Find(j => j.Identifier == jobIdentifier); if (job == null || job.MaxNumber <= 0) continue; var jobFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), jobList.Content.RectTransform), style: "ListBoxElement") { UserData = job }; GUITextBlock jobText = new GUITextBlock(new RectTransform(new Vector2(0.66f, 1.0f), jobFrame.RectTransform, Anchor.CenterRight), i + ". " + job.Name + " ", textAlignment: Alignment.CenterLeft); var jobButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.8f), jobFrame.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.02f, 0.0f) }, isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.03f }; int buttonSize = jobButtonContainer.Rect.Height; GUIButton infoButton = new GUIButton(new RectTransform(new Point(buttonSize, buttonSize), jobButtonContainer.RectTransform), "?") { UserData = job, OnClicked = ViewJobInfo }; GUIButton upButton = new GUIButton(new RectTransform(new Point(buttonSize, buttonSize), jobButtonContainer.RectTransform), "") { UserData = -1, OnClicked = ChangeJobPreference }; new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), upButton.RectTransform, Anchor.Center), GUI.Arrow, scaleToFit: true); GUIButton downButton = new GUIButton(new RectTransform(new Point(buttonSize, buttonSize), jobButtonContainer.RectTransform), "") { UserData = 1, OnClicked = ChangeJobPreference }; new GUIImage(new RectTransform(new Vector2(0.8f, 0.8f), downButton.RectTransform, Anchor.Center), GUI.Arrow, scaleToFit: true) { Rotation = MathHelper.Pi }; } UpdateJobPreferences(jobList); } else { new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), infoContainer.RectTransform), characterInfo.Job.Name, textAlignment: Alignment.Center, wrap: true); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), infoContainer.RectTransform), TextManager.Get("Skills")); foreach (Skill skill in characterInfo.Job.Skills) { Color textColor = Color.White * (0.5f + skill.Level / 200.0f); var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), infoContainer.RectTransform), " - " + TextManager.Get("SkillName." + skill.Identifier) + ": " + (int)skill.Level, textColor); } //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), infoContainer.RectTransform), style: null); new GUIButton(new RectTransform(new Vector2(0.8f, 0.1f), infoContainer.RectTransform, Anchor.BottomCenter), "Create new") { IgnoreLayoutGroups = true, OnClicked = (btn, userdata) => { var confirmation = new GUIMessageBox(TextManager.Get("NewCampaignCharacterHeader"), TextManager.Get("NewCampaignCharacterText"), new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); confirmation.Buttons[0].OnClicked += confirmation.Close; confirmation.Buttons[0].OnClicked += (btn2, userdata2) => { CampaignCharacterDiscarded = true; campaignCharacterInfo = null; UpdatePlayerFrame(null, true); return true; }; confirmation.Buttons[1].OnClicked += confirmation.Close; return true; } }; } } public void SetPlayYourself(bool playYourself) { this.playYourself.Selected = playYourself; if (playYourself) { UpdatePlayerFrame(campaignCharacterInfo, allowEditing: campaignCharacterInfo == null); } else { playerInfoContainer.ClearChildren(); GameMain.NetworkMember.CharacterInfo = null; GameMain.NetworkMember.Character = null; new GUITextBlock(new RectTransform(Vector2.One, playerInfoContainer.RectTransform, Anchor.Center), TextManager.Get("PlayingAsSpectator"), textAlignment: Alignment.Center); } } public void SetAllowSpectating(bool allowSpectating) { //show the player config menu if spectating is not allowed if (!allowSpectating) { SetPlayYourself(true); } //hide "play yourself" tickbox if spectating is not allowed playYourself.Visible = allowSpectating; } public void SetAutoRestart(bool enabled, float timer = 0.0f) { autoRestartBox.Selected = enabled; autoRestartTimer = timer; } public void ToggleAutoRestart() { autoRestartBox.Selected = !autoRestartBox.Selected; ToggleAutoRestart(autoRestartBox); } private bool ToggleAutoRestart(GUITickBox tickBox) { if (GameMain.Server == null) return false; GameMain.Server.AutoRestart = tickBox.Selected; lastUpdateID++; return true; } public void SetMissionType(int missionTypeIndex) { if (missionTypeIndex < 0 || missionTypeIndex >= Enum.GetValues(typeof(MissionType)).Length) return; ((GUITextBlock)missionTypeContainer.GetChild(2)).Text = TextManager.Get("MissionType." + ((MissionType)missionTypeIndex).ToString()); missionTypeContainer.UserData = ((MissionType)missionTypeIndex); } public bool ToggleMissionType(GUIButton button, object userData) { if (GameMain.Server == null) return false; int missionTypeIndex = (int)missionTypeContainer.UserData; missionTypeIndex += (int)userData; if (missionTypeIndex < 0) missionTypeIndex = Enum.GetValues(typeof(MissionType)).Length - 1; if (missionTypeIndex >= Enum.GetValues(typeof(MissionType)).Length) missionTypeIndex = 0; SetMissionType(missionTypeIndex); lastUpdateID++; return true; } public bool ToggleTraitorsEnabled(GUIButton button, object userData) { ToggleTraitorsEnabled((int)userData); return true; } public bool ChangeBotCount(GUIButton button, object userData) { if (GameMain.Server == null) return false; SetBotCount(GameMain.Server.BotCount + (int)userData); return true; } public bool ChangeBotSpawnMode(GUIButton button, object userData) { if (GameMain.Server == null) return false; SetBotSpawnMode(GameMain.Server.BotSpawnMode == BotSpawnMode.Fill ? BotSpawnMode.Normal : BotSpawnMode.Fill); return true; } private bool SelectSub(GUIComponent component, object obj) { if (GameMain.Server == null) return false; lastUpdateID++; var hash = obj is Submarine ? ((Submarine)obj).MD5Hash.Hash : ""; //hash will be null if opening the sub file failed -> don't select the sub if (string.IsNullOrWhiteSpace(hash)) { GUITextBlock submarineTextBlock = component.GetChild(); if (submarineTextBlock != null) { submarineTextBlock.TextColor = Color.DarkRed * 0.8f; submarineTextBlock.CanBeFocused = false; } else { DebugConsole.ThrowError("Failed to select submarine. Selected GUIComponent was of the type \"" + (component == null ? "null" : component.GetType().ToString()) + "\"."); GameAnalyticsManager.AddErrorEventOnce( "NetLobbyScreen.SelectSub:InvalidComponent", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, "Failed to select submarine. Selected GUIComponent was of the type \"" + (component == null ? "null" : component.GetType().ToString()) + "\"."); } StartButton.Enabled = false; return false; } StartButton.Enabled = true; return true; } public void UpdateSubList(GUIComponent subList, List submarines) { if (subList == null) return; subList.ClearChildren(); if (submarines.Count == 0 && GameMain.Server != null) { DebugConsole.ThrowError("No submarines found!"); } foreach (Submarine sub in submarines) { AddSubmarine(subList, sub); } } private void AddSubmarine(GUIComponent subList, Submarine sub) { if (subList is GUIListBox) { subList = ((GUIListBox)subList).Content; } else if (subList is GUIDropDown) { subList = ((GUIDropDown)subList).ListBox.Content; } var frame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), subList.RectTransform) { MinSize = new Point(0, 20) }, style: "ListBoxElement") { ToolTip = sub.Description, UserData = sub }; int buttonSize = (int)(frame.Rect.Height * 0.8f); var subTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1.0f), frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(buttonSize + 5, 0) }, ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 65), textAlignment: Alignment.CenterLeft) { CanBeFocused = false }; var matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name && s.MD5Hash.Hash == sub.MD5Hash.Hash); if (matchingSub == null) matchingSub = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == sub.Name); if (matchingSub == null) { subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f); frame.ToolTip = TextManager.Get("SubNotFound"); } else if (matchingSub.MD5Hash.Hash != sub.MD5Hash.Hash) { subTextBlock.TextColor = new Color(subTextBlock.TextColor, 0.5f); frame.ToolTip = TextManager.Get("SubDoesntMatch"); } else { if (subList == shuttleList || subList == shuttleList.ListBox || subList == shuttleList.ListBox.Content) { subTextBlock.TextColor = new Color(subTextBlock.TextColor, sub.HasTag(SubmarineTag.Shuttle) ? 1.0f : 0.6f); } GUIButton infoButton = new GUIButton(new RectTransform(new Point(buttonSize, buttonSize), frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point((int)(buttonSize * 0.2f), 0) }, "?") { UserData = sub }; infoButton.OnClicked += (component, userdata) => { ((Submarine)userdata).CreatePreviewWindow(new GUIMessageBox("", "", 550, 400)); return true; }; } if (sub.HasTag(SubmarineTag.Shuttle)) { new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), frame.RectTransform, Anchor.CenterRight), TextManager.Get("Shuttle"), textAlignment: Alignment.CenterRight, font: GUI.SmallFont) { TextColor = subTextBlock.TextColor * 0.8f, ToolTip = subTextBlock.ToolTip, CanBeFocused = false }; //make shuttles more dim in the sub list (selecting a shuttle as the main sub is allowed but not recommended) if (subList == this.subList.Content) { subTextBlock.TextColor *= 0.5f; foreach (GUIComponent child in frame.Children) { child.Color *= 0.5f; } } } } public bool VotableClicked(GUIComponent component, object userData) { if (GameMain.Client == null) return false; VoteType voteType; if (component.Parent == GameMain.NetLobbyScreen.SubList.Content) { if (!GameMain.Client.Voting.AllowSubVoting) { if (GameMain.Client.HasPermission(ClientPermissions.SelectSub)) { GameMain.Client.RequestSelectSub(component.Parent.GetChildIndex(component)); return true; } return false; } voteType = VoteType.Sub; } else if (component.Parent == GameMain.NetLobbyScreen.ModeList.Content) { if (!((GameModePreset)userData).Votable) return false; if (!GameMain.Client.Voting.AllowModeVoting) { if (GameMain.Client.HasPermission(ClientPermissions.SelectMode)) { GameMain.Client.RequestSelectMode(component.Parent.GetChildIndex(component)); return true; } return false; } voteType = VoteType.Mode; } else { return false; } GameMain.Client.Vote(voteType, userData); return true; } public bool ChangeServerName(GUITextBox textBox, string text) { if (GameMain.Server == null) return false; ServerName = text; lastUpdateID++; return true; } public bool UpdateServerMessage(GUITextBox textBox, string text) { if (GameMain.Server == null) return false; lastUpdateID++; return true; } public void AddPlayer(string name) { GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), playerList.Content.RectTransform), name, textAlignment: Alignment.CenterLeft) { UserData = name }; if (GameMain.Server != null) lastUpdateID++; } public void RemovePlayer(string name) { GUIComponent child = playerList.Content.GetChildByUserData(name); if (child != null) playerList.RemoveChild(child); if (GameMain.Server != null) lastUpdateID++; } private bool SelectPlayer(GUIComponent component, object obj) { var selectedClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.Name == obj.ToString()); if (selectedClient == null) return false; if (GameMain.Client != null) { if (selectedClient.ID == GameMain.Client.ID) return false; if (!GameMain.Client.HasPermission(ClientPermissions.Ban) && !GameMain.Client.HasPermission(ClientPermissions.Kick) && !GameMain.Client.Voting.AllowVoteKick) { return false; } } playerFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker") { OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) ClosePlayerFrame(btn, userdata); return true; } }; var playerFrameInner = new GUIFrame(new RectTransform(GameMain.Server != null ? new Point(450, 370) : new Point(450, 150), playerFrame.RectTransform, Anchor.Center)); var paddedPlayerFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), playerFrameInner.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.05f }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), text: obj.ToString(), font: GUI.LargeFont); if (GameMain.Server != null) { playerFrame.UserData = selectedClient; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), selectedClient.Connection.RemoteEndPoint.Address.ToString()); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedPlayerFrame.RectTransform), TextManager.Get("Rank")); var rankDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), TextManager.Get("Rank")) { UserData = selectedClient }; foreach (PermissionPreset permissionPreset in PermissionPreset.List) { rankDropDown.AddItem(permissionPreset.Name, permissionPreset, permissionPreset.Description); } rankDropDown.AddItem(TextManager.Get("CustomRank"), null); PermissionPreset currentPreset = PermissionPreset.List.Find(p => p.Permissions == selectedClient.Permissions && p.PermittedCommands.Count == selectedClient.PermittedConsoleCommands.Count && !p.PermittedCommands.Except(selectedClient.PermittedConsoleCommands).Any()); rankDropDown.SelectItem(currentPreset); rankDropDown.OnSelected += (c, userdata) => { PermissionPreset selectedPreset = (PermissionPreset)userdata; if (selectedPreset != null) { var client = playerFrame.UserData as Client; client.SetPermissions(selectedPreset.Permissions, selectedPreset.PermittedCommands); GameMain.Server.UpdateClientPermissions(client); playerFrame = null; SelectPlayer(null, client.Name); } return true; }; var permissionLabels = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), paddedPlayerFrame.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("Permissions")); new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), permissionLabels.RectTransform), TextManager.Get("PermittedConsoleCommands")); var permissionContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), paddedPlayerFrame.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; var permissionsBox = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) { UserData = selectedClient }; foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) { if (permission == ClientPermissions.None) continue; var permissionTick = new GUITickBox(new RectTransform(new Vector2(0.15f, 0.15f), permissionsBox.Content.RectTransform), TextManager.Get("ClientPermission." + permission), font: GUI.SmallFont) { UserData = permission, Selected = selectedClient.HasPermission(permission), OnSelected = (tickBox) => { //reset rank to custom rankDropDown.SelectItem(null); var client = playerFrame.UserData as Client; if (client == null) return false; var thisPermission = (ClientPermissions)tickBox.UserData; if (tickBox.Selected) client.GivePermission(thisPermission); else client.RemovePermission(thisPermission); GameMain.Server.UpdateClientPermissions(client); return true; } }; } var commandList = new GUIListBox(new RectTransform(new Vector2(0.5f, 1.0f), permissionContainer.RectTransform)) { UserData = selectedClient }; foreach (DebugConsole.Command command in DebugConsole.Commands) { var commandTickBox = new GUITickBox(new RectTransform(new Vector2(0.15f, 0.15f), commandList.Content.RectTransform), command.names[0], font: GUI.SmallFont) { Selected = selectedClient.PermittedConsoleCommands.Contains(command), ToolTip = command.help, UserData = command }; commandTickBox.OnSelected += (GUITickBox tickBox) => { //reset rank to custom rankDropDown.SelectItem(null); Client client = playerFrame.UserData as Client; DebugConsole.Command selectedCommand = tickBox.UserData as DebugConsole.Command; if (client == null) return false; if (!tickBox.Selected) { client.PermittedConsoleCommands.Remove(selectedCommand); } else if (!client.PermittedConsoleCommands.Contains(selectedCommand)) { client.PermittedConsoleCommands.Add(selectedCommand); } GameMain.Server.UpdateClientPermissions(client); return true; }; } } var buttonAreaUpper = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true); var buttonAreaLower = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedPlayerFrame.RectTransform), isHorizontal: true); if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Ban)) { var banButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform), TextManager.Get("Ban")) { UserData = obj }; banButton.OnClicked += BanPlayer; banButton.OnClicked += ClosePlayerFrame; var rangebanButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaUpper.RectTransform), TextManager.Get("BanRange")) { UserData = obj }; rangebanButton.OnClicked += BanPlayerRange; rangebanButton.OnClicked += ClosePlayerFrame; } if (GameMain.Client != null && GameMain.Client.Voting.AllowVoteKick && selectedClient != null) { var kickVoteButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform), TextManager.Get("VoteToKick")) { Enabled = !selectedClient.HasKickVoteFromID(GameMain.Client.ID), OnClicked = GameMain.Client.VoteForKick, UserData = selectedClient }; } if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Kick)) { var kickButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform), TextManager.Get("Kick")) { UserData = obj }; kickButton.OnClicked = KickPlayer; kickButton.OnClicked += ClosePlayerFrame; } var closeButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonAreaLower.RectTransform, Anchor.BottomRight), TextManager.Get("Close")) { IgnoreLayoutGroups = true, OnClicked = ClosePlayerFrame }; return false; } private bool ClosePlayerFrame(GUIButton button, object userData) { playerFrame = null; return true; } public bool KickPlayer(GUIButton button, object userData) { if (userData == null || GameMain.NetworkMember == null) return false; GameMain.NetworkMember.CreateKickReasonPrompt(userData.ToString(), false); return false; } public bool BanPlayer(GUIButton button, object userData) { if (userData == null || GameMain.NetworkMember == null) return false; GameMain.NetworkMember.CreateKickReasonPrompt(userData.ToString(), true); return false; } public bool BanPlayerRange(GUIButton button, object userData) { if (userData == null || GameMain.NetworkMember == null) return false; GameMain.NetworkMember.CreateKickReasonPrompt(userData.ToString(), true, true); return false; } public void ClearPlayers() { playerList.ClearChildren(); if (GameMain.Server != null) lastUpdateID++; } public override void AddToGUIUpdateList() { base.AddToGUIUpdateList(); if (campaignContainer.Visible) { chatFrame.AddToGUIUpdateList(); playerListFrame.AddToGUIUpdateList(); } playerFrame?.AddToGUIUpdateList(); campaignSetupUI?.AddToGUIUpdateList(); jobInfoFrame?.AddToGUIUpdateList(); } public List GetSubList() { List subs = new List(); foreach (GUIComponent component in subList.Content.Children) { if (component.UserData is Submarine) subs.Add((Submarine)component.UserData); } return subs; } public override void Update(double deltaTime) { base.Update(deltaTime); if (campaignSetupUI != null) { if (!campaignSetupUI.Visible) campaignSetupUI = null; } if (autoRestartTimer != 0.0f && autoRestartBox.Selected) { autoRestartTimer = Math.Max(autoRestartTimer - (float)deltaTime, 0.0f); } } public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) { graphics.Clear(Color.Black); GUI.DrawBackgroundSprite(spriteBatch, backgroundSprite); spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable); if (campaignUI != null) { campaignUI.MapContainer.DrawAuto(spriteBatch); } GUI.Draw(Cam, spriteBatch); spriteBatch.End(); } public void NewChatMessage(ChatMessage message) { float prevSize = chatBox.BarSize; while (chatBox.Content.CountChildren > 20) { chatBox.RemoveChild(chatBox.Content.Children.First()); } GUITextBlock msg = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), chatBox.Content.RectTransform), text: (message.Type == ChatMessageType.Private ? TextManager.Get("PrivateMessageTag") + " " : "") + message.TextWithSender, textColor: message.Color, color: ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, wrap: true, font: GUI.SmallFont) { UserData = message, CanBeFocused = false, }; if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f; } private Memento generatedHeads = new Memento(); private bool SwitchHead(GUIButton button, object userData) { if (GameMain.NetworkMember.CharacterInfo == null) return true; int dir = (int)userData; var info = GameMain.NetworkMember.CharacterInfo; if (generatedHeads.Current == null) { // Add the current head in the memory generatedHeads.Store(info.Head); } if (dir == 1) { // Try redo, if not possible, generate new var previousHead = generatedHeads.Redo(); if (previousHead == info.Head || previousHead == null) { // Generate new and add to the list // If the head id is the same, regenerate until it's not // The counter is there to prevent stack overflow if we for some reason cannot get unique ids (e.g. an issue with the head id range or simply if there is no heads defined). int newHeadId = previousHead.HeadSpriteId; int counter = 0; while (newHeadId == previousHead.HeadSpriteId && counter < 10) { newHeadId = info.GetRandomHeadID(); counter++; } info.Head = new CharacterInfo.HeadInfo(newHeadId) { gender = GameMain.Config.CharacterGender }; generatedHeads.Store(info.Head); } else { info.Head = previousHead; } } else { // Undo, if not possible, the button should be disabled var previousHead = generatedHeads.Undo(); if (previousHead != info.Head && previousHead != null) { info.Head = previousHead; } } info.ReloadHeadAttachments(); StoreHead(); GameMain.Config.Save(); faceSelectionLeft.Enabled = generatedHeads.UndoCount > 0; return true; } private bool SwitchGender(GUIButton button, object obj) { generatedHeads.Clear(); Gender gender = (Gender)obj; var info = GameMain.NetworkMember.CharacterInfo; info.Gender = gender; info.SetRandomHead(); info.LoadHeadAttachments(); StoreHead(); GameMain.Config.Save(); return true; } private void StoreHead() { var info = GameMain.NetworkMember.CharacterInfo; var config = GameMain.Config; config.CharacterRace = info.Race; config.CharacterGender = info.Gender; config.CharacterHeadIndex = info.HeadSpriteId; config.CharacterHairIndex = info.HairIndex; config.CharacterBeardIndex = info.BeardIndex; config.CharacterMoustacheIndex = info.MoustacheIndex; config.CharacterFaceAttachmentIndex = info.FaceAttachmentIndex; } public void SelectMode(int modeIndex) { if (modeIndex < 0 || modeIndex >= modeList.Content.CountChildren || modeList.SelectedIndex == modeIndex) return; if (((GameModePreset)modeList.Content.GetChild(modeIndex).UserData).Identifier == "multiplayercampaign") { if (GameMain.Server != null) { campaignSetupUI = MultiPlayerCampaign.StartCampaignSetup(); return; } } else { ToggleCampaignMode(false); } modeList.Select(modeIndex, true); missionTypeContainer.Visible = SelectedMode != null && SelectedMode.Identifier == "mission"; } private bool SelectMode(GUIComponent component, object obj) { if (GameMain.NetworkMember == null || obj == modeList.SelectedData) return false; GameModePreset modePreset = obj as GameModePreset; if (modePreset == null) return false; //TODO: don't use the name of the game mode to determine its type missionTypeContainer.Visible = modePreset.Identifier == "mission"; if (modePreset.Identifier == "multiplayercampaign") { //campaign selected and the campaign view has not been set up yet // -> don't select the mode yet and start campaign setup if (GameMain.Server != null && !campaignContainer.Visible) { campaignSetupUI = MultiPlayerCampaign.StartCampaignSetup(); return false; } } else { ToggleCampaignMode(false); } lastUpdateID++; return true; } public void ToggleCampaignView(bool enabled) { campaignContainer.Visible = enabled; defaultModeContainer.Visible = !enabled; } public void ToggleCampaignMode(bool enabled) { ToggleCampaignView(enabled); if (!enabled) { campaignCharacterInfo = null; CampaignCharacterDiscarded = false; UpdatePlayerFrame(null); } subList.Enabled = !enabled && AllowSubSelection; shuttleList.Enabled = !enabled && AllowSubSelection; seedBox.Enabled = !enabled && GameMain.Server != null; if (campaignViewButton != null) campaignViewButton.Visible = enabled; if (StartButton != null) StartButton.Visible = !enabled && GameMain.Server != null; if (enabled) { if (campaignUI == null || campaignUI.Campaign != GameMain.GameSession.GameMode) { campaignContainer.ClearChildren(); campaignUI = new CampaignUI(GameMain.GameSession.GameMode as CampaignMode, campaignContainer) { StartRound = () => { GameMain.Server.StartGame(); } }; campaignUI.MapContainer.RectTransform.NonScaledSize = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); var backButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.08f), campaignContainer.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.02f) }, TextManager.Get("Back"), style: "GUIButtonLarge"); backButton.OnClicked += (btn, obj) => { ToggleCampaignView(false); return true; }; var restartText = new GUITextBlock(new RectTransform(new Vector2(0.25f, 0.1f), campaignContainer.RectTransform, Anchor.BottomRight), "", font: GUI.SmallFont) { TextGetter = AutoRestartText }; } modeList.Select(2, true); } else { campaignUI = null; } if (GameMain.Server != null) { lastUpdateID++; } } private bool SelectSeed(GUITextBox textBox, string seed) { if (GameMain.Server == null) return false; if (string.IsNullOrWhiteSpace(seed)) return false; LevelSeed = seed; return true; } private bool ViewJobInfo(GUIButton button, object obj) { JobPrefab jobPrefab = button.UserData as JobPrefab; if (jobPrefab == null) return false; jobInfoFrame = jobPrefab.CreateInfoFrame(); GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.05f), jobInfoFrame.GetChild(2).GetChild(0).RectTransform, Anchor.BottomRight), TextManager.Get("Close")) { OnClicked = CloseJobInfo }; jobInfoFrame.OnClicked = (btn, userdata) => { if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) CloseJobInfo(btn, userdata); return true; }; return true; } private bool CloseJobInfo(GUIButton button, object obj) { jobInfoFrame = null; return true; } private bool ChangeJobPreference(GUIButton button, object obj) { GUIComponent jobText = button.Parent.Parent; int index = jobList.Content.GetChildIndex(jobText); int newIndex = index + (int)obj; if (newIndex < 0 || newIndex > jobList.Content.CountChildren - 1) return false; jobText.RectTransform.RepositionChildInHierarchy(newIndex); UpdateJobPreferences(jobList); return true; } private void UpdateJobPreferences(GUIListBox listBox) { listBox.Deselect(); List jobNamePreferences = new List(); for (int i = 0; i < listBox.Content.CountChildren; i++) { float a = (float)(i - 1) / 3.0f; a = Math.Min(a, 3); Color color = new Color(1.0f - a, (1.0f - a) * 0.6f, 0.0f, 0.3f); GUIComponent child = listBox.Content.GetChild(i); child.Color = color; child.HoverColor = color; child.SelectedColor = color; (child.GetChild()).Text = (i + 1) + ". " + (child.UserData as JobPrefab).Name; jobNamePreferences.Add((child.UserData as JobPrefab).Identifier); } if (!GameMain.Config.JobPreferences.SequenceEqual(jobNamePreferences)) { GameMain.Config.JobPreferences = jobNamePreferences; GameMain.Config.Save(); } } public Pair FailedSelectedSub; public Pair FailedSelectedShuttle; public bool TrySelectSub(string subName, string md5Hash, GUIListBox subList) { if (GameMain.Client == null) return false; //already downloading the selected sub file if (GameMain.Client.FileReceiver.ActiveTransfers.Any(t => t.FileName == subName + ".sub")) { return false; } Submarine sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName && m.MD5Hash.Hash == md5Hash); if (sub == null) sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName); var matchingListSub = subList.Content.GetChildByUserData(sub); if (matchingListSub != null) { if (subList.Parent is GUIDropDown subDropDown) { subDropDown.SelectItem(sub); } else { subList.OnSelected -= VotableClicked; subList.Select(subList.Content.GetChildIndex(matchingListSub), true); subList.OnSelected += VotableClicked; } if (subList == SubList) FailedSelectedSub = null; else FailedSelectedShuttle = null; } if (sub == null || sub.MD5Hash.Hash != md5Hash) { if (subList == SubList) FailedSelectedSub = new Pair(subName, md5Hash); else FailedSelectedShuttle = new Pair(subName, md5Hash); string errorMsg = ""; if (sub == null) { errorMsg = TextManager.Get("SubNotFoundError").Replace("[subname]", subName) + " "; } else if (sub.MD5Hash.Hash == null) { errorMsg = TextManager.Get("SubLoadError").Replace("[subname]", subName) + " "; if (matchingListSub != null) matchingListSub.GetChild().TextColor = Color.Red; } else { errorMsg = TextManager.Get("SubDoesntMatchError").Replace("[subname]", sub.Name).Replace("[myhash]", sub.MD5Hash.ShortHash).Replace("[serverhash]", Md5Hash.GetShortHash(md5Hash)) + " "; } errorMsg += TextManager.Get("DownloadSubQuestion"); //already showing a message about the same sub if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "request" + subName)) { return false; } var requestFileBox = new GUIMessageBox(TextManager.Get("DownloadSubLabel"), errorMsg, new string[] { TextManager.Get("Yes"), TextManager.Get("No") }, 400, 300); requestFileBox.UserData = "request" + subName; requestFileBox.Buttons[0].UserData = new string[] { subName, md5Hash }; requestFileBox.Buttons[0].OnClicked += requestFileBox.Close; requestFileBox.Buttons[0].OnClicked += (GUIButton button, object userdata) => { string[] fileInfo = (string[])userdata; GameMain.Client?.RequestFile(FileTransferType.Submarine, fileInfo[0], fileInfo[1]); return true; }; requestFileBox.Buttons[1].OnClicked += requestFileBox.Close; return false; } return true; } } }