diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs index ecc9cb4f6..a2e3ff53f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs @@ -535,6 +535,17 @@ namespace Barotrauma return closestCharacter; } + public bool ShouldLockHud() + { + if (this != controlled) { return false; } + + //lock if using a controller, except if we're also using a connection panel in the same item + return + SelectedConstruction != null && + SelectedConstruction?.GetComponent()?.User == this && + SelectedConstruction?.GetComponent()?.User != this; + } + partial void UpdateProjSpecific(float deltaTime, Camera cam) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs index 62ef87c61..b4ec1eb65 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterHUD.cs @@ -92,7 +92,7 @@ namespace Barotrauma if (!character.IsUnconscious && character.Stun <= 0.0f) { - if (character.Info != null) + if (character.Info != null && !character.ShouldLockHud()) { bool mouseOnPortrait = HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null; if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked()) @@ -170,11 +170,7 @@ namespace Barotrauma { if (GUI.DisableHUD) { return; } - character.CharacterHealth.Alignment = Alignment.Right; - /*if (Screen.Selected == GameMain.GameScreen) - { - GUI.InfoAreaBackground.Draw(spriteBatch, Vector2.Zero, scale: GUI.Scale); - }*/ + character.CharacterHealth.Alignment = Alignment.Right; if (GameMain.GameSession?.CrewManager != null) { @@ -309,7 +305,7 @@ namespace Barotrauma character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), targetWidth: HUDLayoutSettings.PortraitArea.Width); character.Info.DrawJobIcon(spriteBatch); } - mouseOnPortrait = HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition); + mouseOnPortrait = HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); if (mouseOnPortrait) { GUI.UIGlow.Draw(spriteBatch, HUDLayoutSettings.PortraitArea, GUI.Style.Green * 0.5f); @@ -318,7 +314,7 @@ namespace Barotrauma if (ShouldDrawInventory(character)) { character.Inventory.Locked = LockInventory(character); - character.Inventory.DrawThis(spriteBatch); + character.Inventory.DrawOwn(spriteBatch); character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ? CharacterInventory.Layout.Default : CharacterInventory.Layout.Right; @@ -333,7 +329,7 @@ namespace Barotrauma { ///character.Inventory.CurrentLayout = Alignment.Left; character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left; - character.SelectedCharacter.Inventory.DrawThis(spriteBatch); + character.SelectedCharacter.Inventory.DrawOwn(spriteBatch); } else { @@ -417,11 +413,7 @@ namespace Barotrauma { if (character?.Inventory == null || !character.AllowInput || character.LockHands) { return true; } - //lock if using a controller, except if we're also using a connection panel in the same item - return - character.SelectedConstruction != null && - character.SelectedConstruction?.GetComponent()?.User == character && - character.SelectedConstruction?.GetComponent()?.User != character; + return character.ShouldLockHud(); } private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs index f7e086d0b..fa6333a09 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs @@ -276,7 +276,7 @@ namespace Barotrauma }; healthShadowSize = 1.0f; - healthInterfaceFrame = new GUIFrame(new RectTransform(new Vector2(0.85f * 1.1f, 0.66f * 0.85f * 1.1f), GUI.Canvas, anchor: Anchor.Center, scaleBasis: ScaleBasis.Smallest), style: "ItemUI"); + healthInterfaceFrame = new GUIFrame(new RectTransform(new Vector2(0.7f, 0.55f), GUI.Canvas, anchor: Anchor.Center, scaleBasis: ScaleBasis.Smallest), style: "ItemUI"); var healthInterfaceLayout = new GUILayoutGroup(new RectTransform(Vector2.One / 1.05f, healthInterfaceFrame.RectTransform, anchor: Anchor.Center), true); @@ -331,16 +331,15 @@ namespace Barotrauma }, (dt, component) => { - medUIExtraAnimState += dt * 10.0f; - while (medUIExtraAnimState >= 16.0f) + if (!GameMain.Instance.Paused) { - medUIExtraAnimState -= 16.0f; + medUIExtraAnimState = (medUIExtraAnimState + dt * 10.0f) % 16.0f; } }); GUILayoutGroup selectedLimbLayout = new GUILayoutGroup(new RectTransform(Vector2.One, rightSide.RectTransform)); - selectedLimbText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.08f), selectedLimbLayout.RectTransform), "", font: GUI.LargeFont, textAlignment: Alignment.Center) + selectedLimbText = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.08f), selectedLimbLayout.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Center) { AutoScaleHorizontal = true }; @@ -454,11 +453,16 @@ namespace Barotrauma OnClicked = (button, userData) => { Character selectedCharacter = Character.Controlled?.SelectedCharacter; - if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f)) return false; + if (selectedCharacter == null || (!selectedCharacter.IsUnconscious && selectedCharacter.Stun <= 0.0f)) + { + return false; + } Character.Controlled.AnimController.Anim = (Character.Controlled.AnimController.Anim == AnimController.Animation.CPR) ? AnimController.Animation.None : AnimController.Animation.CPR; + button.Selected = Character.Controlled.AnimController.Anim == AnimController.Animation.CPR; + selectedCharacter.AnimController.ResetPullJoints(); if (GameMain.Client != null) @@ -468,6 +472,7 @@ namespace Barotrauma return true; }, + ToolTip = TextManager.Get("doctor.cprobjective"), Visible = false }; @@ -540,12 +545,10 @@ namespace Barotrauma switch (alignment) { case Alignment.Left: - healthInterfaceFrame.RectTransform.Anchor = Anchor.CenterLeft; - healthInterfaceFrame.RectTransform.Pivot = Pivot.CenterLeft; + healthInterfaceFrame.RectTransform.SetPosition(Anchor.CenterLeft); break; case Alignment.Right: - healthInterfaceFrame.RectTransform.Anchor = Anchor.CenterRight; - healthInterfaceFrame.RectTransform.Pivot = Pivot.CenterRight; + healthInterfaceFrame.RectTransform.SetPosition(Anchor.CenterRight); break; } healthInterfaceFrame.RectTransform.RecalculateChildren(false); @@ -833,7 +836,7 @@ namespace Barotrauma treatmentLayout.Recalculate(); - lowSkillIndicator.Color = new Color(lowSkillIndicator.Color, MathHelper.Lerp(0.1f, 1.0f, (float)(Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 2.0f)); + lowSkillIndicator.Color = new Color(lowSkillIndicator.Color, MathHelper.Lerp(0.5f, 1.0f, (float)(Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) / 2.0f)); if (Inventory.draggingItem != null) { @@ -868,8 +871,8 @@ namespace Barotrauma Rectangle hoverArea = Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarAreaLeft); - if (Character.AllowInput && UseHealthWindow && - Character.SelectedConstruction?.GetComponent()?.User != Character && + healthBar.CanBeFocused = healthBarShadow.CanBeFocused = !Character.ShouldLockHud(); + if (Character.AllowInput && UseHealthWindow && healthBar.Enabled && healthBar.CanBeFocused && hoverArea.Contains(PlayerInput.MousePosition) && Inventory.SelectedSlot == null) { healthBar.State = GUIComponent.ComponentState.Hover; @@ -983,7 +986,7 @@ namespace Barotrauma AfflictionPrefab afflictionPrefab = affliction.Prefab; Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize)); - if (afflictionIconRect.Contains(PlayerInput.MousePosition)) + if (afflictionIconRect.Contains(PlayerInput.MousePosition) && !Character.ShouldLockHud()) { highlightedIcon = statusIcon; highlightedIconPos = afflictionIconRect.Center.ToVector2(); @@ -1023,7 +1026,7 @@ namespace Barotrauma if (highlightedIcon != null) { GUI.DrawString(spriteBatch, - alignment == Alignment.Left ? highlightedIconPos + new Vector2(60 * GUI.Scale, 5) : highlightedIconPos + new Vector2(-iconSize / 2, iconSize / 2), + alignment == Alignment.Left ? highlightedIconPos + new Vector2(60 * GUI.Scale, 5) : highlightedIconPos + new Vector2(iconSize * 0.4f, 0.0f), highlightedIcon.Second, Color.White * 0.8f, Color.Black * 0.5f); } @@ -1127,6 +1130,7 @@ namespace Barotrauma { var child = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), afflictionIconContainer.Content.RectTransform, Anchor.TopCenter)) { + Stretch = true, UserData = affliction }; @@ -1145,7 +1149,7 @@ namespace Barotrauma buttonToSelect = button; } - var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.9f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true) + var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true) { Color = GetAfflictionIconColor(affliction.Prefab, affliction), CanBeFocused = false @@ -1166,7 +1170,11 @@ namespace Barotrauma afflictionEffectColor = GUI.Style.Green; } - new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.1f), child.RectTransform), 0.0f, afflictionEffectColor, style: "CharacterHealthBar") + var nameText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), child.RectTransform), + affliction.Prefab.Name, font: GUI.SmallFont, textAlignment: Alignment.Center, style: "GUIToolTip"); + nameText.Text = ToolBox.LimitString(nameText.Text, nameText.Font, nameText.Rect.Width); + + new GUIProgressBar(new RectTransform(new Vector2(1.0f, 0.1f), child.RectTransform), 0.0f, afflictionEffectColor, style: "GUIAfflictionBar") { UserData = "afflictionstrength" }; @@ -1240,14 +1248,14 @@ namespace Barotrauma { CanBeFocused = false }; - var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUI.LargeFont) + var afflictionStrength = new GUITextBlock(new RectTransform(new Vector2(0.35f, 0.6f), labelContainer.RectTransform), "", textAlignment: Alignment.TopRight, font: GUI.SubHeadingFont) { - Padding = Vector4.Zero, UserData = "strength", CanBeFocused = false }; var vitality = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), labelContainer.RectTransform, Anchor.BottomRight), "", textAlignment: Alignment.BottomRight) { + Padding = afflictionStrength.Padding, IgnoreLayoutGroups = true, UserData = "vitality", CanBeFocused = false @@ -1557,7 +1565,10 @@ namespace Barotrauma private void UpdateLimbIndicators(float deltaTime, Rectangle drawArea) { - limbIndicatorOverlayAnimState += deltaTime * 8.0f; + if (!GameMain.Instance.Paused) + { + limbIndicatorOverlayAnimState += deltaTime * 8.0f; + } highlightedLimbIndex = -1; int i = 0; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs index 767b91ebc..feb8dee03 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs @@ -10,7 +10,7 @@ namespace Barotrauma { public GUIButton CreateInfoFrame(int variant) { - int width = 500, height = 450; + int width = 500, height = 400; GUIButton backFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker"); GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), backFrame.RectTransform, Anchor.Center)); @@ -32,7 +32,7 @@ namespace Barotrauma font: GUI.SmallFont); } - if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; } + /*if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; } var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) }) { @@ -47,7 +47,7 @@ namespace Barotrauma new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform), " - " + (count == 1 ? itemPrefab.Name : itemPrefab.Name + " x" + count), font: GUI.SmallFont); - } + }*/ return backFrame; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index e255c764f..96d33dfc3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -397,7 +397,7 @@ namespace Barotrauma Submarine.Load(string.Join(" ", args), true); } GameMain.SubEditorScreen.Select(); - })); + }, isCheat: true)); commands.Add(new Command("editparticles|particleeditor", "editparticles/particleeditor: Switch to the Particle Editor to edit particle effects.", (string[] args) => { @@ -1099,6 +1099,22 @@ namespace Barotrauma } } + foreach (Submarine sub in Submarine.SavedSubmarines) + { + string nameIdentifier = "submarine.name." + sub.Name.ToLowerInvariant(); + if (!tags[language].Contains(nameIdentifier)) + { + if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet(); } + missingTags[nameIdentifier].Add(language); + } + string descriptionIdentifier = "submarine.description." + sub.Name.ToLowerInvariant(); + if (!tags[language].Contains(descriptionIdentifier)) + { + if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet(); } + missingTags[descriptionIdentifier].Add(language); + } + } + foreach (AfflictionPrefab affliction in AfflictionPrefab.List) { string nameIdentifier = "afflictionname." + affliction.Identifier; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs index 2ef419166..d84769b3c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs @@ -29,10 +29,6 @@ namespace Barotrauma { _toggleOpen = GameMain.Config.ChatOpen = value; if (value) hideableElements.Visible = true; - foreach (GUIComponent child in ToggleButton.Children) - { - child.SpriteEffects = _toggleOpen ? SpriteEffects.FlipHorizontally : SpriteEffects.None; - } } } private float openState; @@ -56,7 +52,7 @@ namespace Barotrauma public GUITextBox InputBox { get; private set; } - public GUIButton ToggleButton { get; private set; } + public GUIButton ToggleButton; private GUIButton showNewMessagesButton; @@ -79,18 +75,10 @@ namespace Barotrauma var chatBoxHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.875f), hideableElements.RectTransform), style: "ChatBox"); chatBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), chatBoxHolder.RectTransform, Anchor.CenterRight), style: null); - ToggleButton = new GUIButton(new RectTransform(new Point(toggleButtonWidth, HUDLayoutSettings.ChatBoxArea.Height), parent.RectTransform), - style: "UIToggleButton"); - - ToggleButton.OnClicked += (GUIButton btn, object userdata) => - { - ToggleOpen = !ToggleOpen; - return true; - }; - InputBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.125f), hideableElements.RectTransform, Anchor.BottomLeft), style: "ChatTextBox") { + OverflowClip = true, Font = GUI.SmallFont, MaxTextLength = ChatMessage.MaxLength }; @@ -110,7 +98,8 @@ namespace Barotrauma return true; }; chatSendButton.RectTransform.AbsoluteOffset = new Point((int)(InputBox.Rect.Height * 0.15f), 0); - InputBox.TextBlock.RectTransform.MaxSize = new Point((int)(InputBox.Rect.Width - chatSendButton.Rect.Width * 1.25f), int.MaxValue); + InputBox.TextBlock.RectTransform.MaxSize + = new Point((int)(InputBox.Rect.Width - chatSendButton.Rect.Width * 1.25f - InputBox.TextBlock.Padding.Z), int.MaxValue); showNewMessagesButton = new GUIButton(new RectTransform(new Vector2(1f, 0.125f), GUIFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, -0.125f) }, TextManager.Get("chat.shownewmessages")); showNewMessagesButton.OnClicked += (GUIButton btn, object userdata) => @@ -317,10 +306,7 @@ namespace Barotrauma GUIFrame.RectTransform.NonScaledSize -= new Point(toggleButtonWidth, 0); GUIFrame.RectTransform.AbsoluteOffset += new Point(toggleButtonWidth, 0); - ToggleButton.RectTransform.NonScaledSize = new Point(toggleButtonWidth, HUDLayoutSettings.ChatBoxArea.Height); - ToggleButton.RectTransform.AbsoluteOffset = new Point(HUDLayoutSettings.ChatBoxArea.Left - toggleButtonWidth, HUDLayoutSettings.ChatBoxArea.Y); - - popupMessageOffset = ToggleButton.Rect.Width + GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width + GUIFrame.Rect.Width; + popupMessageOffset = GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width + GUIFrame.Rect.Width; } public void Update(float deltaTime) @@ -348,6 +334,11 @@ namespace Barotrauma showNewMessagesButton.Visible = false; } + if (ToggleButton != null) + { + ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height); + } + if (ToggleOpen) { openState += deltaTime * 5.0f; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs index 01387431c..4ab4c2ca3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs @@ -133,7 +133,6 @@ namespace Barotrauma public static ScalableFont LargeFont => Style?.LargeFont; public static ScalableFont SubHeadingFont => Style?.SubHeadingFont; public static ScalableFont DigitalFont => Style?.DigitalFont; - public static ScalableFont HotkeyFont => Style?.HotkeyFont; public static ScalableFont CJKFont { get; private set; } @@ -163,8 +162,6 @@ namespace Barotrauma get { return arrow; } } - public static Sprite InfoAreaBackground; - public static bool SettingsMenuOpen { get { return settingsMenuOpen; } @@ -260,7 +257,6 @@ namespace Barotrauma arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(392, 393, 49, 45), new Vector2(0.5f, 0.5f)); SpeechBubbleIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f)); BrokenIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(898, 386, 123, 123), new Vector2(0.5f, 0.5f)); - InfoAreaBackground = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(290, 320, 400, 300), new Vector2(0.0f, 0.0f)); } /// @@ -782,9 +778,21 @@ namespace Barotrauma if (GUIScrollBar.DraggingBar != null) { return GUIScrollBar.DraggingBar.Bar.HoverCursor; } // Wire cursors - if (ConnectionPanel.HighlightedWire != null) { return CursorState.Hand; } - if (Wire.DraggingWire != null) { return CursorState.Dragging; } - if (Connection.DraggingConnected != null) { return CursorState.Dragging; } + if (Character.Controlled != null) + { + if (Character.Controlled.SelectedConstruction?.GetComponent() != null) + { + if (Connection.DraggingConnected != null) + { + return CursorState.Dragging; + } + else if (ConnectionPanel.HighlightedWire != null) + { + return CursorState.Hand; + } + } + if (Wire.DraggingWire != null) { return CursorState.Dragging; } + } if (c == null || c is GUICustomComponent) { @@ -794,7 +802,7 @@ namespace Barotrauma case CharacterEditorScreen editor: return editor.GetMouseCursorState(); // Portrait area during gameplay - case GameScreen _ when HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition): + case GameScreen _ when HUDLayoutSettings.PortraitArea.Contains(PlayerInput.MousePosition) && !(Character.Controlled?.ShouldLockHud() ?? true): return CursorState.Hand; // Sub editor drag and highlight case SubEditorScreen editor: @@ -989,7 +997,6 @@ namespace Barotrauma Debug.Assert(updateList.Count == updateListSet.Count); updateList.ForEach(c => c.UpdateAuto(deltaTime)); UpdateMessages(deltaTime); - UpdateInput(); } private static void UpdateMessages(float deltaTime) @@ -1029,32 +1036,6 @@ namespace Barotrauma messages.RemoveAll(m => m.Timer <= 0.0f); } - private static void UpdateInput() - { - if (PlayerInput.KeyHit(InputType.ToggleInventory)) - { - if (Character.Controlled?.Inventory != null) - { - Character.Controlled.Inventory.ToggleInventory(); - } - } - - if (PlayerInput.KeyHit(Keys.Escape) && GameMain.WindowActive) - { - HandleEscFunctionality(); - } - -#if DEBUG - if (GameMain.NetworkMember == null) - { - if (PlayerInput.KeyHit(Keys.P) && !(KeyboardDispatcher.Subscriber is GUITextBox)) - { - DebugConsole.Paused = !DebugConsole.Paused; - } - } -#endif - } - #region Element drawing public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color) @@ -1860,50 +1841,6 @@ namespace Barotrauma #endregion #region Misc - private static void HandleEscFunctionality() - { - // Check if a text input is selected. - if (KeyboardDispatcher.Subscriber != null) - { - if (KeyboardDispatcher.Subscriber is GUITextBox textBox) - { - textBox.Deselect(); - } - KeyboardDispatcher.Subscriber = null; - } - //if a verification prompt (are you sure you want to x) is open, close it - else if (GUIMessageBox.VisibleBox as GUIMessageBox != null && - GUIMessageBox.VisibleBox.UserData as string == "verificationprompt") - { - ((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); - } - else if (Tutorials.Tutorial.Initialized && Tutorials.Tutorial.ContentRunning) - { - (GameMain.GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI(); - } - else if (PauseMenuOpen) - { - TogglePauseMenu(); - } - //open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC - else if ((Character.Controlled == null || !itemHudActive()) - //TODO: do we need to check Inventory.SelectedSlot? - && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null - && !CrewManager.IsCommandInterfaceOpen) - { - // Otherwise toggle pausing, unless another window/interface is open. - TogglePauseMenu(); - } - - bool itemHudActive() - { - if (Character.Controlled?.SelectedConstruction == null) { return false; } - return - Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) || - ((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false); - } - } - public static void TogglePauseMenu() { if (Screen.Selected == GameMain.MainMenuScreen) return; @@ -2102,6 +2039,12 @@ namespace Barotrauma sounds[soundIndex]?.Play(null, "ui"); } + + public static bool IsFourByThree() + { + float aspectRatio = HorizontalAspectRatio; + return aspectRatio > 1.3f && aspectRatio < 1.4f; + } #endregion } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIColorSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIColorSettings.cs index dd7f2765f..7b402000f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIColorSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIColorSettings.cs @@ -5,23 +5,20 @@ namespace Barotrauma public class GUIColorSettings { // Inventory - public static readonly Color InventorySlotColor = new Color(27, 140, 132); - public static readonly Color InventorySlotEquippedColor = new Color(211, 227, 217); - public static readonly Color EquipmentSlotEmptyColor = new Color(152, 148, 128); - public static readonly Color EquipmentSlotColor = new Color(225, 211, 189); - public static readonly Color EquipmentSlotIconColor = new Color(99, 70, 64); + public static Color EquipmentSlotIconColor = new Color(99, 70, 64); // Health HUD - public static readonly Color BuffColorLow = Color.LightGreen; - public static readonly Color BuffColorMedium = Color.Green; - public static readonly Color BuffColorHigh = Color.DarkGreen; + public static Color BuffColorLow = Color.LightGreen; + public static Color BuffColorMedium = Color.Green; + public static Color BuffColorHigh = Color.DarkGreen; - public static readonly Color DebuffColorLow = Color.DarkSalmon; - public static readonly Color DebuffColorMedium = Color.Red; - public static readonly Color DebuffColorHigh = Color.DarkRed; + public static Color DebuffColorLow = Color.DarkSalmon; + public static Color DebuffColorMedium = Color.Red; + public static Color DebuffColorHigh = Color.DarkRed; + + public static Color HealthBarColorLow = Color.Red; + public static Color HealthBarColorMedium = Color.Orange; + public static Color HealthBarColorHigh = new Color(78, 114, 88); - public static readonly Color HealthBarColorLow = Color.Red; - public static readonly Color HealthBarColorMedium = Color.Orange; - public static readonly Color HealthBarColorHigh = new Color(78, 114, 88); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs index c291a99bc..77bb51dc1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs @@ -772,24 +772,29 @@ namespace Barotrauma if (rectTransform != null) { - if (style.Width.HasValue) - { - RectTransform.MinSize = new Point(style.Width.Value, RectTransform.MinSize.Y); - RectTransform.MaxSize = new Point(style.Width.Value, RectTransform.MaxSize.Y); - if (rectTransform.IsFixedSize) { RectTransform.Resize(new Point(style.Width.Value, rectTransform.NonScaledSize.Y)); } - } - if (style.Height.HasValue) - { - RectTransform.MinSize = new Point(RectTransform.MinSize.X, style.Height.Value); - RectTransform.MaxSize = new Point(RectTransform.MaxSize.X, style.Height.Value); - if (rectTransform.IsFixedSize) { RectTransform.Resize(new Point(rectTransform.NonScaledSize.X, style.Height.Value)); } - } + ApplySizeRestrictions(style); } this.style = style; } + public void ApplySizeRestrictions(GUIComponentStyle style) + { + if (style.Width.HasValue) + { + RectTransform.MinSize = new Point(style.Width.Value, RectTransform.MinSize.Y); + RectTransform.MaxSize = new Point(style.Width.Value, RectTransform.MaxSize.Y); + if (rectTransform.IsFixedSize) { RectTransform.Resize(new Point(style.Width.Value, rectTransform.NonScaledSize.Y)); } + } + if (style.Height.HasValue) + { + RectTransform.MinSize = new Point(RectTransform.MinSize.X, style.Height.Value); + RectTransform.MaxSize = new Point(RectTransform.MaxSize.X, style.Height.Value); + if (rectTransform.IsFixedSize) { RectTransform.Resize(new Point(rectTransform.NonScaledSize.X, style.Height.Value)); } + } + } + public static GUIComponent FromXML(XElement element, RectTransform parent) { GUIComponent component = null; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs index a6b66afe2..ceeeb5192 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs @@ -1,16 +1,16 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; +using System.Linq; namespace Barotrauma { public class GUIProgressBar : GUIComponent { private bool isHorizontal; - - private GUIFrame frame, slider; + private readonly GUIFrame frame, slider; private float barSize; - private bool showFrame; + private readonly bool showFrame; public delegate float ProgressGetterHandler(); public ProgressGetterHandler ProgressGetter; @@ -55,6 +55,45 @@ namespace Barotrauma Enabled = true; } + /// + /// Get the area the slider should be drawn inside + /// + /// 0 = empty, 1 = full + public Rectangle GetSliderRect(float fillAmount) + { + Rectangle sliderArea = new Rectangle( + frame.Rect.X + (int)style.Padding.X, + frame.Rect.Y + (int)style.Padding.Y, + (int)(frame.Rect.Width - style.Padding.X - style.Padding.Z), + (int)(frame.Rect.Height - style.Padding.Y - style.Padding.W)); + + Vector4 sliceBorderSizes = Vector4.Zero; + if (slider.sprites.ContainsKey(slider.State) && (slider.sprites[slider.State].First()?.Slice ?? false)) + { + var slices = slider.sprites[slider.State].First().Slices; + sliceBorderSizes = new Vector4(slices[0].Width, slices[0].Height, slices[8].Width, slices[8].Height); + sliceBorderSizes *= slider.sprites[slider.State].First().GetSliceBorderScale(sliderArea.Size); + } + + Rectangle sliderRect = IsHorizontal ? + new Rectangle( + sliderArea.X + (int)sliceBorderSizes.X, + sliderArea.Y, + (int)((sliderArea.Width - sliceBorderSizes.X - sliceBorderSizes.Z) * fillAmount), + sliderArea.Height) + : + new Rectangle( + sliderArea.X, + (int)(sliderArea.Bottom - (sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount - sliceBorderSizes.W), + sliderArea.Width, + (int)((sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount)); + + sliderRect.Width = Math.Max(sliderRect.Width, 1); + sliderRect.Height = Math.Max(sliderRect.Height, 1); + + return sliderRect; + } + protected override void Draw(SpriteBatch spriteBatch) { if (!Visible) { return; } @@ -75,14 +114,7 @@ namespace Barotrauma } } - Rectangle sliderRect = new Rectangle( - frame.Rect.X + (int)style.Padding.X, - (int)(frame.Rect.Y + (int)style.Padding.Y + (isHorizontal ? 0 : frame.Rect.Height * (1.0f - barSize))), - isHorizontal ? (int)((frame.Rect.Width - style.Padding.X - style.Padding.Z) * barSize) : frame.Rect.Width, - isHorizontal ? (int)(frame.Rect.Height - style.Padding.Y - style.Padding.W) : (int)(frame.Rect.Height * barSize)); - - sliderRect.Width = Math.Max(sliderRect.Width, 1); - sliderRect.Height = Math.Max(sliderRect.Height, 1); + var sliderRect = GetSliderRect(barSize); slider.RectTransform.AbsoluteOffset = new Point((int)style.Padding.X, (int)style.Padding.Y); slider.RectTransform.MaxSize = new Point( diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs index 531281f4e..9d14505ed 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs @@ -24,7 +24,6 @@ namespace Barotrauma public ScalableFont LargeFont { get; private set; } public ScalableFont SubHeadingFont { get; private set; } public ScalableFont DigitalFont { get; private set; } - public ScalableFont HotkeyFont { get; private set; } public Dictionary ForceFontUpperCase { @@ -149,10 +148,6 @@ namespace Barotrauma DigitalFont = LoadFont(subElement, graphicsDevice); ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false); break; - case "hotkeyfont": - HotkeyFont = LoadFont(subElement, graphicsDevice); - ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false); - break; case "objectivetitle": case "subheading": SubHeadingFont = LoadFont(subElement, graphicsDevice); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/HUDLayoutSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/HUDLayoutSettings.cs index 279bf851e..8aac435a9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/HUDLayoutSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/HUDLayoutSettings.cs @@ -132,18 +132,17 @@ namespace Barotrauma int messageAreaWidth = GameMain.GraphicsWidth / 3; MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height); - int toggleButtonWidth = (int)(ChatBox.ToggleButtonWidthRaw * GUI.Scale); - int chatBoxWidth = (int)(475 * GUI.Scale); + bool isFourByThree = GUI.IsFourByThree(); + int chatBoxWidth = !isFourByThree ? (int)(475 * GUI.Scale) : (int)(375 * GUI.Scale); int chatBoxHeight = (int)Math.Max(GameMain.GraphicsHeight * 0.22f, 150); - ChatBoxArea = new Rectangle(Padding + toggleButtonWidth, GameMain.GraphicsHeight - Padding - chatBoxHeight, chatBoxWidth, chatBoxHeight); + ChatBoxArea = new Rectangle(Padding, GameMain.GraphicsHeight - Padding - chatBoxHeight, chatBoxWidth, chatBoxHeight); int objectiveAnchorWidth = (int)(250 * GUI.Scale); int objectiveAnchorOffsetY = (int)(150 * GUI.Scale); ObjectiveAnchor = new Rectangle(Padding, ChatBoxArea.Y - objectiveAnchorOffsetY, objectiveAnchorWidth, 0); var crewAreaY = AfflictionAreaLeft.Bottom + Padding; - var crewAreaHeight = ObjectiveAnchor.Top - Padding - crewAreaY; - CrewArea = new Rectangle(Padding, crewAreaY, (int)Math.Max(400 * GUI.Scale, 150), crewAreaHeight); + CrewArea = new Rectangle(Padding, crewAreaY, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding - crewAreaY); InventoryAreaLower = new Rectangle(Padding, inventoryTopY, GameMain.GraphicsWidth - Padding * 2, GameMain.GraphicsHeight - inventoryTopY); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/RectTransform.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/RectTransform.cs index b9b533589..d61cc17e9 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/RectTransform.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/RectTransform.cs @@ -52,9 +52,9 @@ namespace Barotrauma { parent.children.Add(this); RecalculateAll(false, true, true); - ParentChanged?.Invoke(parent); Parent.ChildrenChanged?.Invoke(this); } + ParentChanged?.Invoke(parent); } } @@ -512,7 +512,7 @@ namespace Barotrauma } } - private bool RemoveFromHierarchy(bool displayErrors = true, bool recalculate = true) + private bool RemoveFromHierarchy(bool displayErrors = true) { if (Parent == null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs index 0186b5160..1425432c1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs @@ -41,7 +41,7 @@ namespace Barotrauma /// How much the borders of a sliced sprite are allowed to scale /// You may for example want to prevent a 1-pixel border from scaling down (and disappearing) on small resolutions /// - private float minBorderScale = 0.1f, maxBorderScale = 10.0f; + private readonly float minBorderScale = 0.1f, maxBorderScale = 10.0f; public bool CrossFadeIn { get; private set; } = true; public bool CrossFadeOut { get; private set; } = true; @@ -95,6 +95,19 @@ namespace Barotrauma } } + /// + /// Get the scale of the sliced sprite's borders when it's draw inside an area of a specific size + /// + public float GetSliceBorderScale(Point drawSize) + { + if (!Slice) { return 1.0f; } + + Vector2 scale = new Vector2( + MathHelper.Clamp((float)drawSize.X / (Slices[0].Height + Slices[6].Height), 0, 1), + MathHelper.Clamp((float)drawSize.Y / (Slices[0].Width + Slices[2].Width), 0, 1)); + return MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale); + } + public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None) { if (Sprite.Texture == null) @@ -107,23 +120,17 @@ namespace Barotrauma { Vector2 pos = new Vector2(rect.X, rect.Y); - Vector2 scale = Vector2.One; - - scale.Y = MathHelper.Clamp((float)rect.Height / (Slices[0].Height + Slices[6].Height), 0, 1); - scale.X = MathHelper.Clamp((float)rect.Width / (Slices[0].Width + Slices[2].Width), 0, 1); - - scale.X = scale.Y = - MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale); - int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale.Y); - int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale.X); + float scale = GetSliceBorderScale(rect.Size); + int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale); + int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale); for (int x = 0; x < 3; x++) { - int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale.X); + int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale); if (width <= 0) { continue; } for (int y = 0; y < 3; y++) { - int height = (int)(y == 1 ? centerHeight : Slices[x + y * 3].Height * scale.Y); + int height = (int)(y == 1 ? centerHeight : Slices[x + y * 3].Height * scale); if (height <= 0) { continue; } spriteBatch.Draw(Sprite.Texture, diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/VideoPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/VideoPlayer.cs index 63f966936..af99de492 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/VideoPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/VideoPlayer.cs @@ -176,10 +176,10 @@ namespace Barotrauma background.RectTransform.NonScaledSize = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); - videoFrame.RectTransform.NonScaledSize += scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize); - videoView.RectTransform.NonScaledSize += scaledVideoResolution; + videoFrame.RectTransform.NonScaledSize = scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize); + videoView.RectTransform.NonScaledSize = scaledVideoResolution; - title.RectTransform.NonScaledSize += new Point(scaledTextWidth, scaledTitleHeight); + title.RectTransform.NonScaledSize = new Point(scaledTextWidth, scaledTitleHeight); title.RectTransform.AbsoluteOffset = new Point((int)(5 * GUI.Scale), (int)(10 * GUI.Scale)); if (textSettings != null && !string.IsNullOrEmpty(textSettings.Text)) @@ -187,7 +187,7 @@ namespace Barotrauma textSettings.Text = ToolBox.WrapText(textSettings.Text, scaledTextWidth, GUI.Font); int wrappedHeight = textSettings.Text.Split('\n').Length * scaledTextHeight; - textFrame.RectTransform.NonScaledSize += new Point(scaledTextWidth + scaledBorderSize, wrappedHeight + scaledBorderSize + scaledButtonSize.Y + scaledTitleHeight); + textFrame.RectTransform.NonScaledSize = new Point(scaledTextWidth + scaledBorderSize, wrappedHeight + scaledBorderSize + scaledButtonSize.Y + scaledTitleHeight); if (useTextOnRightSide) { @@ -198,7 +198,7 @@ namespace Barotrauma textFrame.RectTransform.AbsoluteOffset = new Point(0, scaledVideoResolution.Y + scaledBorderSize * 2); } - textContent.RectTransform.NonScaledSize += new Point(scaledTextWidth, wrappedHeight); + textContent.RectTransform.NonScaledSize = new Point(scaledTextWidth, wrappedHeight); textContent.RectTransform.AbsoluteOffset = new Point(0, scaledBorderSize + scaledTitleHeight); } @@ -210,7 +210,7 @@ namespace Barotrauma objectiveText.RectTransform.AbsoluteOffset = new Point(scaledXOffset, textContent.RectTransform.Rect.Height + objectiveTitle.Rect.Height + (int)(scaledTextHeight * 2.25f)); textFrame.RectTransform.NonScaledSize += new Point(0, scaledObjectiveFrameHeight); - objectiveText.RectTransform.NonScaledSize += new Point(textFrame.Rect.Width, scaledTextHeight); + objectiveText.RectTransform.NonScaledSize = new Point(textFrame.Rect.Width, scaledTextHeight); objectiveTitle.Visible = objectiveText.Visible = true; } else diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index 6ff841088..ed5021ac0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -765,6 +765,60 @@ namespace Barotrauma SoundPlayer.Update((float)Timing.Step); + if (PlayerInput.KeyHit(Keys.Escape) && WindowActive) + { + // Check if a text input is selected. + if (GUI.KeyboardDispatcher.Subscriber != null) + { + if (GUI.KeyboardDispatcher.Subscriber is GUITextBox textBox) + { + textBox.Deselect(); + } + GUI.KeyboardDispatcher.Subscriber = null; + } + //if a verification prompt (are you sure you want to x) is open, close it + else if (GUIMessageBox.VisibleBox as GUIMessageBox != null && + GUIMessageBox.VisibleBox.UserData as string == "verificationprompt") + { + ((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); + } + else if (Tutorial.Initialized && Tutorial.ContentRunning) + { + (GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI(); + } + else if (GUI.PauseMenuOpen) + { + GUI.TogglePauseMenu(); + } + //open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC + else if ((Character.Controlled == null || !itemHudActive()) + //TODO: do we need to check Inventory.SelectedSlot? + && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null + && !CrewManager.IsCommandInterfaceOpen) + { + // Otherwise toggle pausing, unless another window/interface is open. + GUI.TogglePauseMenu(); + } + + bool itemHudActive() + { + if (Character.Controlled?.SelectedConstruction == null) { return false; } + return + Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) || + ((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false); + } + } + +#if DEBUG + if (GameMain.NetworkMember == null) + { + if (PlayerInput.KeyHit(Keys.P) && !(GUI.KeyboardDispatcher.Subscriber is GUITextBox)) + { + DebugConsole.Paused = !DebugConsole.Paused; + } + } +#endif + GUI.ClearUpdateList(); Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) && (NetworkMember == null || !NetworkMember.GameStarted); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs index fdbd03911..738f2b4a8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs @@ -31,7 +31,7 @@ namespace Barotrauma private GUIFrame guiFrame; private GUIFrame crewArea; private GUIListBox crewList; - private GUIButton toggleCrewButton; + private GUIButton commandButton, toggleCrewButton; private float crewListOpenState; private bool toggleCrewListOpen = true; @@ -59,7 +59,7 @@ namespace Barotrauma public List OrderOptionButtons = new List(); - private Sprite jobIndicatorBackground, previousOrderArrow, cancelIcon, crewMemberBackground; + private Sprite jobIndicatorBackground, previousOrderArrow, cancelIcon; #endregion @@ -112,11 +112,12 @@ namespace Barotrauma }; var buttonSize = new Point((int)(182.0f / 99.0f * buttonHeight), buttonHeight); - var commandButton = new GUIButton( + commandButton = new GUIButton( new RectTransform(buttonSize, parent: crewAreaWithButtons.RectTransform), style: "CommandButton") { - ToolTip = TextManager.Get("inputtype.command"), + // TODO: Update keybind if it's changed + ToolTip = TextManager.Get("inputtype.command") + " (" + GameMain.Config.KeyBindText(InputType.Command) + ")", OnClicked = (button, userData) => { ToggleCommandUI(); @@ -124,23 +125,6 @@ namespace Barotrauma } }; - var keybindText = new GUITextBlock(new RectTransform(new Vector2(0.9f), commandButton.RectTransform, Anchor.Center), - "", - font: GUI.SmallFont, - textAlignment: Alignment.TopLeft, - textColor: GUI.Style.TextColorBright, - style: null) - { - TextGetter = () => - { - //hide the text if using a long non-default keybind - string txt = GameMain.Config.KeyBindText(InputType.Command); - return txt.Length > 2 ? "" : txt; - }, - Padding = Vector4.One * 3, - CanBeFocused = false - }; - // AbsoluteOffset is set in UpdateProjectSpecific based on crewListOpenState crewList = new GUIListBox( new RectTransform( @@ -150,6 +134,8 @@ namespace Barotrauma isScrollBarOnDefaultSide: false) { AutoHideScrollBar = false, + OnSelected = (component, userData) => false, + SelectMultiple = false, Spacing = (int)(GUI.Scale * 10) }; @@ -172,7 +158,6 @@ namespace Barotrauma jobIndicatorBackground = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(0, 512, 128, 128)); previousOrderArrow = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(128, 512, 128, 128)); cancelIcon = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(512, 384, 128, 128)); - crewMemberBackground = new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(0, 728, 320, 40)); #region Chatbox @@ -224,6 +209,14 @@ namespace Barotrauma #endregion #region Reports + var chatBox = ChatBox ?? GameMain.Client?.ChatBox; + chatBox.ToggleButton = new GUIButton(new RectTransform(new Point((int)(182f * GUI.Scale * 0.4f), (int)(99f * GUI.Scale * 0.4f)), guiFrame.RectTransform), style: "ChatToggleButton"); + chatBox.ToggleButton.RectTransform.AbsoluteOffset = new Point(0, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height); + chatBox.ToggleButton.OnClicked += (GUIButton btn, object userdata) => + { + chatBox.ToggleOpen = !chatBox.ToggleOpen; + return true; + }; var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); if (reports.None()) @@ -231,14 +224,17 @@ namespace Barotrauma DebugConsole.ThrowError("No valid orders for report buttons found! Cannot create report buttons. The orders for the report buttons must have 'targetallcharacters' attribute enabled and a valid 'symbolsprite' defined."); return; } + ReportButtonFrame = new GUILayoutGroup(new RectTransform( - new Point((HUDLayoutSettings.ChatBoxArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.ChatBoxArea.Height), guiFrame.RectTransform)) + new Point((HUDLayoutSettings.ChatBoxArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.ChatBoxArea.Height - chatBox.ToggleButton.Rect.Height), guiFrame.RectTransform)) { AbsoluteSpacing = (int)(5 * GUI.Scale), UserData = "reportbuttons", CanBeFocused = false }; + ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(0, -chatBox.ToggleButton.Rect.Height); + //report buttons foreach (Order order in reports) { @@ -247,7 +243,7 @@ namespace Barotrauma { OnClicked = (GUIButton button, object userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!CanIssueOrders) { return false; } SetCharacterOrder(null, order, null, Character.Controlled); var visibleHulls = new List(Character.Controlled.GetVisibleHulls()); foreach (var hull in visibleHulls) @@ -261,7 +257,7 @@ namespace Barotrauma ToolTip = order.Name }; - new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") + new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "InnerGlowCircular") { Color = GUI.Style.Red * 0.8f, HoverColor = GUI.Style.Red * 1.0f, @@ -381,17 +377,18 @@ namespace Barotrauma private void AddCharacterToCrewList(Character character) { + if (character == null) { return; } + int width = crewList.Content.Rect.Width - HUDLayoutSettings.Padding; int height = Math.Max(32, (int)((1.0f / 8.0f) * width)); - var background = new GUIImage( + var background = new GUIFrame( new RectTransform(new Point(width, height), parent: crewList.Content.RectTransform, anchor: Anchor.TopRight), - crewMemberBackground, - scaleToFit: true) + style: "CrewListBackground") { UserData = character }; - var iconRelativeSize = (float)height / background.Rect.Width; + var iconRelativeWidth = (float)height / background.Rect.Width; var layoutGroup = new GUILayoutGroup( new RectTransform(Vector2.One, parent: background.RectTransform), @@ -399,12 +396,17 @@ namespace Barotrauma childAnchor: Anchor.CenterLeft) { CanBeFocused = false, - RelativeSpacing = 0.1f * iconRelativeSize, + RelativeSpacing = 0.1f * iconRelativeWidth, UserData = character }; + // "Padding" to prevent member-specific command button from overlapping job indicator + var commandButtonAbsoluteHeight = Math.Min(40.0f, 0.67f * background.Rect.Height); + var paddingRelativeWidth = 0.35f * commandButtonAbsoluteHeight / background.Rect.Width; + new GUIFrame(new RectTransform(new Vector2(paddingRelativeWidth, 1.0f), layoutGroup.RectTransform), style: null); + var jobIconBackground = new GUIImage( - new RectTransform(new Vector2(iconRelativeSize, 0.9f), layoutGroup.RectTransform), + new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), jobIndicatorBackground, scaleToFit: true) { @@ -427,27 +429,46 @@ namespace Barotrauma }; } - var relativeWidth = 1.0f - 4.5f * iconRelativeSize - 5 * layoutGroup.RelativeSpacing; + var nameRelativeWidth = 1.0f - paddingRelativeWidth - 3.7f * iconRelativeWidth; var font = layoutGroup.Rect.Width < 150 ? GUI.SmallFont : GUI.Font; - new GUITextBlock( + var nameBlock = new GUITextBlock( new RectTransform( - new Vector2(relativeWidth, 1.0f), - layoutGroup.RectTransform), - ToolBox.LimitString(character.Name, font, (int)(relativeWidth * layoutGroup.Rect.Width)), + new Vector2(nameRelativeWidth, 1.0f), + layoutGroup.RectTransform) + { + MaxSize = new Point(150, background.Rect.Height) + }, + ToolBox.LimitString(character.Name, font, (int)(nameRelativeWidth * layoutGroup.Rect.Width)), font: font, textColor: character.Info?.Job?.Prefab?.UIColor) { CanBeFocused = false }; + var nameActualRealtiveWidth = Math.Min(nameRelativeWidth * background.Rect.Width, 150) / background.Rect.Width; var characterButton = new GUIButton( new RectTransform( - new Vector2(iconRelativeSize + layoutGroup.RelativeSpacing + relativeWidth, 1.0f), + new Vector2(paddingRelativeWidth + 0.8f * iconRelativeWidth + nameActualRealtiveWidth + 2 * layoutGroup.RelativeSpacing, 1.0f), background.RectTransform), style: null) { UserData = character }; + // Only create a tooltip if the name doesn't fit the name block + if (nameBlock.Text.EndsWith("...")) + { + var characterTooltip = character.Name; + if (character.Info?.Job?.Name != null) { characterTooltip += " (" + character.Info.Job.Name + ")"; }; + characterButton.ToolTip = characterTooltip; + if (character.Info?.Job?.Prefab != null) + { + characterButton.TooltipColorData = new List() { new ColorData() + { + Color = character.Info.Job.Prefab.UIColor, + EndIndex = characterTooltip.Length - 1 + }}; + } + } if (IsSinglePlayer) { characterButton.OnClicked = CharacterClicked; @@ -459,13 +480,13 @@ namespace Barotrauma } new GUIImage( - new RectTransform(new Vector2(0.5f * iconRelativeSize, 0.5f), layoutGroup.RectTransform), + new RectTransform(new Vector2(0.5f * iconRelativeWidth, 0.5f), layoutGroup.RectTransform), style: "VerticalLine") { CanBeFocused = false }; - var soundIcons = new GUIFrame(new RectTransform(new Vector2(iconRelativeSize, 0.8f), layoutGroup.RectTransform), style: null) + var soundIcons = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null) { CanBeFocused = false, UserData = "soundicons" @@ -488,6 +509,17 @@ namespace Barotrauma UserData = "soundicondisabled", Visible = false }; + + new GUIButton(new RectTransform(new Point((int)commandButtonAbsoluteHeight), background.RectTransform), style: "CrewListCommandButton") + { + ToolTip = TextManager.Get("inputtype.command"), + OnClicked = (component, userData) => + { + if (!CanIssueOrders) { return false; } + CreateCommandUI(character); + return true; + } + }; } /// @@ -716,15 +748,14 @@ namespace Barotrauma var orderFrame = new GUIButton( new RectTransform( - new Vector2(layoutGroup.GetChildByUserData("job").RectTransform.RelativeSize.X, 0.8f), + layoutGroup.GetChildByUserData("job").RectTransform.RelativeSize, layoutGroup.RectTransform), style: null) { UserData = new OrderInfo(order, option), OnClicked = (button, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } - + if (!CanIssueOrders) { return false; } SetCharacterOrder(character, dismissedOrderPrefab, null, Character.Controlled); return true; } @@ -739,7 +770,7 @@ namespace Barotrauma UserData = "cancel", Visible = false }; - orderFrame.RectTransform.RepositionChildInHierarchy(3); + orderFrame.RectTransform.RepositionChildInHierarchy(4); characterFrame.SetAsFirstChild(); } @@ -750,14 +781,14 @@ namespace Barotrauma var previousOrderInfo = new OrderInfo(currentOrderInfo); var prevOrderFrame = new GUIButton( new RectTransform( - new Vector2(characterComponent.GetChildByUserData("job").RectTransform.RelativeSize.X, 0.8f), + characterComponent.GetChildByUserData("job").RectTransform.RelativeSize, characterComponent.RectTransform), style: null) { UserData = previousOrderInfo, OnClicked = (button, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!CanIssueOrders) { return false; } var orderInfo = (OrderInfo)userData; SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, Character.Controlled); return true; @@ -785,7 +816,7 @@ namespace Barotrauma { CanBeFocused = false }; - prevOrderFrame.RectTransform.RepositionChildInHierarchy(GetCurrentOrderComponent(characterComponent) != null ? 4 : 3); + prevOrderFrame.RectTransform.RepositionChildInHierarchy(GetCurrentOrderComponent(characterComponent) != null ? 5 : 4); } private GUIComponent GetCurrentOrderComponent(GUILayoutGroup characterComponent) @@ -874,18 +905,20 @@ namespace Barotrauma public void SelectNextCharacter() { - if (!AllowCharacterSwitch) { return; } - if (GameMain.IsMultiplayer) { return; } - if (characters.None()) { return; } - SelectCharacter(characters[TryAdjustIndex(1)]); + if (!AllowCharacterSwitch || GameMain.IsMultiplayer || characters.None()) { return; } + if (crewList.Content.GetChild(TryAdjustIndex(1))?.UserData is Character character) + { + SelectCharacter(character); + } } public void SelectPreviousCharacter() { - if (!AllowCharacterSwitch) { return; } - if (GameMain.IsMultiplayer) { return; } - if (characters.None()) { return; } - SelectCharacter(characters[TryAdjustIndex(-1)]); + if (!AllowCharacterSwitch || GameMain.IsMultiplayer || characters.None()) { return; } + if (crewList.Content.GetChild(TryAdjustIndex(-1))?.UserData is Character character) + { + SelectCharacter(character); + } } private void SelectCharacter(Character character) @@ -902,8 +935,9 @@ namespace Barotrauma private int TryAdjustIndex(int amount) { - int index = Character.Controlled == null ? 0 : characters.IndexOf(Character.Controlled) + amount; - int lastIndex = characters.Count - 1; + int index = Character.Controlled == null ? 0 : + crewList.Content.GetChildIndex(crewList.Content.GetChildByUserData(Character.Controlled)) + amount; + int lastIndex = crewList.Content.CountChildren - 1; if (index > lastIndex) { index = 0; @@ -934,39 +968,31 @@ namespace Barotrauma #region Command UI - if (PlayerInput.KeyDown(InputType.Command) && (GUI.KeyboardDispatcher.Subscriber == null || GUI.KeyboardDispatcher.Subscriber == crewList) && - (!GameMain.IsMultiplayer || (GameMain.IsMultiplayer && (Character.Controlled != null || DebugConsole.CheatsEnabled))) && - commandFrame == null && !clicklessSelectionActive) - { - bool canIssueOrders = false; - if (Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f) - { - WifiComponent radio = GetHeadset(Character.Controlled, true); - canIssueOrders = radio != null && radio.CanTransmit(); - } + WasCommandInterfaceDisabledThisUpdate = false; - if (canIssueOrders) - { - CreateCommandUI(GUI.MouseOn?.UserData as Character); - clicklessSelectionActive = isOpeningClick = true; - } + if (PlayerInput.KeyDown(InputType.Command) && (GUI.KeyboardDispatcher.Subscriber == null || GUI.KeyboardDispatcher.Subscriber == crewList) && + commandFrame == null && !clicklessSelectionActive && CanIssueOrders) + { + CreateCommandUI(GUI.MouseOn?.UserData as Character); + clicklessSelectionActive = isOpeningClick = true; } if (commandFrame != null) { void ResetNodeSelection(GUIButton newSelectedNode = null) { + if (commandFrame == null) { return; } selectedNode?.Children.ForEach(c => c.Color = c.HoverColor * nodeColorMultiplier); selectedNode = newSelectedNode; timeSelected = 0; isSelectionHighlighted = false; } - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) + if (!CanIssueOrders) { DisableCommandUI(); } - else if (PlayerInput.RightButtonClicked() && characterContext == null && + else if (PlayerInput.SecondaryMouseButtonClicked() && characterContext == null && (optionNodes.Any(n => GUI.IsMouseOn(n.Item1)) || shortcutNodes.Any(n => GUI.IsMouseOn(n)))) { var node = optionNodes.Find(n => GUI.IsMouseOn(n.Item1))?.Item1; @@ -988,7 +1014,7 @@ namespace Barotrauma } // TODO: Consider using HUD.CloseHUD() instead of KeyHit(Escape), the former method is also used for health UI else if ((PlayerInput.KeyHit(InputType.Command) && selectedNode == null && !clicklessSelectionActive) || - PlayerInput.KeyHit(InputType.Deselect) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) + PlayerInput.KeyHit(InputType.Deselect) || PlayerInput.KeyHit(Keys.Escape)) { DisableCommandUI(); } @@ -1093,11 +1119,24 @@ namespace Barotrauma } } } - else if (PlayerInput.KeyUp(InputType.Command)) + else if (!PlayerInput.KeyDown(InputType.Command)) { clicklessSelectionActive = false; } + // TODO: Expand crew list to use command button's space when it's not visible + if (!IsSinglePlayer && commandButton != null) + { + if (!CanIssueOrders && commandButton.Visible) + { + commandButton.Visible = false; + } + else if (CanIssueOrders && !commandButton.Visible) + { + commandButton.Visible = true; + } + } + #endregion if (GUI.DisableUpperHUD) { return; } @@ -1141,40 +1180,40 @@ namespace Barotrauma if (child.UserData is Character character) { child.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID; - if (child.Visible && child.FindChild(c => c is GUILayoutGroup) is GUILayoutGroup layoutGroup) + if (child.Visible) { - if (GetCurrentOrderComponent(layoutGroup) is GUIComponent orderButton && + if (character == Character.Controlled && child.State != GUIComponent.ComponentState.Selected) + { + crewList.Select(character, force: true); + } + if (child.FindChild(c => c is GUILayoutGroup) is GUILayoutGroup layoutGroup) + { + if (GetCurrentOrderComponent(layoutGroup) is GUIComponent orderButton && orderButton.GetChildByUserData("colorsource") is GUIComponent orderIcon && orderButton.GetChildByUserData("cancel") is GUIComponent cancelIcon) - { - cancelIcon.Visible = GUI.IsMouseOn(orderIcon); - } - if (layoutGroup.GetChildByUserData("soundicons")? - .FindChild(c => c.UserData is Pair pair && pair.First == "soundicon") is GUIImage soundIcon) - { - VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime); + { + cancelIcon.Visible = GUI.IsMouseOn(orderIcon); + } + if (layoutGroup.GetChildByUserData("soundicons")? + .FindChild(c => c.UserData is Pair pair && pair.First == "soundicon") is GUIImage soundIcon) + { + VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime); + } } } } } crewArea.RectTransform.AbsoluteOffset = Vector2.SmoothStep( - new Vector2(-crewArea.Rect.Width - HUDLayoutSettings.Padding, 0.0f), - Vector2.Zero, - crewListOpenState).ToPoint(); + new Vector2(-crewArea.Rect.Width - HUDLayoutSettings.Padding, 0.0f), + Vector2.Zero, + crewListOpenState).ToPoint(); crewListOpenState = ToggleCrewListOpen ? Math.Min(crewListOpenState + deltaTime * 2.0f, 1.0f) : Math.Max(crewListOpenState - deltaTime * 2.0f, 0.0f); - if (GUI.KeyboardDispatcher.Subscriber == null && - PlayerInput.KeyHit(InputType.CrewOrders) && - characters.Contains(Character.Controlled)) + if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(InputType.CrewOrders)) { - //deselect construction unless it's the ladders the character is climbing - if (Character.Controlled != null && !Character.Controlled.IsClimbing) - { - Character.Controlled.SelectedConstruction = null; - } ToggleCrewListOpen = !ToggleCrewListOpen; } @@ -1189,7 +1228,14 @@ namespace Barotrauma { get { - return GameMain.GameSession?.CrewManager?.commandFrame != null; + if (GameMain.GameSession?.CrewManager == null) + { + return false; + } + else + { + return GameMain.GameSession.CrewManager.commandFrame != null || GameMain.GameSession.CrewManager.WasCommandInterfaceDisabledThisUpdate; + } } } private GUIFrame commandFrame, targetFrame; @@ -1222,9 +1268,23 @@ namespace Barotrauma private Order dismissedOrderPrefab; private Character characterContext; private Point shorcutCenterNodeOffset; + private bool WasCommandInterfaceDisabledThisUpdate { get; set; } + private bool CanIssueOrders + { + get + { + return Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f; + } + } + + private bool CanSomeoneHearCharacter() + { + return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled)); + } private void CreateCommandUI(Character characterContext = null) { + if (commandFrame != null) { DisableCommandUI(); } CharacterHealth.OpenHealthWindow = null; ScaleCommandUI(); commandFrame = new GUIFrame( @@ -1233,7 +1293,7 @@ namespace Barotrauma color: Color.Transparent); background = new GUIImage( new RectTransform(Vector2.One, commandFrame.RectTransform, anchor: Anchor.Center), - Order.CommandBackground); + "CommandBackground"); background.Color = background.Color * 0.8f; this.characterContext = characterContext; @@ -1243,7 +1303,7 @@ namespace Barotrauma startNode = new GUIButton( new RectTransform(centerNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null); - CreateNodeIcon(startNode.RectTransform, Order.StartNode, Color.White); + CreateNodeIcon(startNode.RectTransform, "CommandStartNode"); } else { @@ -1254,7 +1314,7 @@ namespace Barotrauma // Container new GUIImage( new RectTransform(Vector2.One, startNode.RectTransform, anchor: Anchor.Center), - Order.NodeContainer, + "CommandNodeContainer", scaleToFit: true) { Color = characterContext.Info.Job.Prefab.UIColor * nodeColorMultiplier, @@ -1290,18 +1350,9 @@ namespace Barotrauma { if (commandFrame == null) { - if ((!GameMain.IsMultiplayer || (GameMain.IsMultiplayer && (Character.Controlled != null || DebugConsole.CheatsEnabled)))) + if (CanIssueOrders) { - bool canIssueOrders = false; - if (Character.Controlled != null && Character.Controlled.SpeechImpediment < 100.0f) - { - WifiComponent radio = GetHeadset(Character.Controlled, true); - canIssueOrders = radio != null && radio.CanTransmit(); - } - if (canIssueOrders) - { - CreateCommandUI(); - } + CreateCommandUI(); } } else @@ -1387,6 +1438,7 @@ namespace Barotrauma public void DisableCommandUI() { if (commandFrame == null) { return; } + WasCommandInterfaceDisabledThisUpdate = true; RemoveOptionNodes(); historyNodes.Clear(); nodeConnectors = null; @@ -1400,7 +1452,7 @@ namespace Barotrauma background = null; commandFrame = null; extraOptionCharacters.Clear(); - clicklessSelectionActive = isOpeningClick = isSelectionHighlighted = false; + isOpeningClick = isSelectionHighlighted = false; characterContext = null; returnNodeHotkey = expandNodeHotkey = Keys.None; if (Character.Controlled != null) @@ -1573,12 +1625,12 @@ namespace Barotrauma }; node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration); - if (Order.OrderCategoryIcons.TryGetValue(category, out Sprite sprite)) + if (Order.OrderCategoryIcons.TryGetValue(category, out Tuple sprite)) { var tooltip = TextManager.Get("ordercategorytitle." + category.ToString().ToLower()); var categoryDescription = TextManager.Get("ordercategorydescription." + category.ToString(), true); if (!string.IsNullOrWhiteSpace(categoryDescription)) { tooltip += "\n" + categoryDescription; } - CreateNodeIcon(node.RectTransform, sprite, Color.White, tooltip: tooltip); + CreateNodeIcon(node.RectTransform, sprite.Item1, sprite.Item2, tooltip: tooltip); } CreateHotkeyIcon(node.RectTransform, hotkey % 10); optionNodes.Add(new Tuple(node, Keys.D0 + hotkey % 10)); @@ -1659,7 +1711,7 @@ namespace Barotrauma shortcutCenterNode = new GUIButton( new RectTransform(shortcutCenterNodeSize, parent: commandFrame.RectTransform, anchor: Anchor.Center), style: null); - CreateNodeIcon(shortcutCenterNode.RectTransform, Order.ShortcutNode, Color.Red); + CreateNodeIcon(shortcutCenterNode.RectTransform, "CommandShortcutNode"); foreach (GUIComponent c in shortcutCenterNode.Children) { c.HoverColor = c.Color; @@ -1682,11 +1734,11 @@ namespace Barotrauma var orders = Order.PrefabList.FindAll(o => o.Category == orderCategory && !o.TargetAllCharacters); var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance, GetCircumferencePointCount(orders.Count), GetFirstNodeAngle(orders.Count)); - for(int i = 0; i < orders.Count; i++) + for (int i = 0; i < orders.Count; i++) { optionNodes.Add(new Tuple( CreateOrderNode(nodeSize, commandFrame.RectTransform, offsets[i].ToPoint(), orders[i], (i + 1) % 10), - Keys.D0 + (i + 1) % 10)); + CanSomeoneHearCharacter() ? Keys.D0 + (i + 1) % 10 : Keys.None)); } } @@ -1700,10 +1752,11 @@ namespace Barotrauma node.RectTransform.MoveOverTime(offset, CommandNodeAnimDuration); + var canSomeoneHearCharacter = CanSomeoneHearCharacter(); var hasOptions = order.ItemComponentType != null || order.ItemIdentifiers.Length > 0 || order.Options.Length > 1; node.OnClicked = (button, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!canSomeoneHearCharacter || !CanIssueOrders) { return false; } var o = userData as Order; // TODO: Consider defining orders' or order categories' quick-assignment possibility in the XML if (o.Category == OrderCategory.Movement && characterContext == null) @@ -1721,10 +1774,20 @@ namespace Barotrauma } return true; }; - CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, - tooltip: hasOptions || characterContext != null ? order.Name : - order.Name + "\nLMB: " + TextManager.Get("commandui.quickassigntooltip") + "\nRMB: " + TextManager.Get("commandui.manualassigntooltip")); - if (hotkey >= 0) { CreateHotkeyIcon(node.RectTransform, hotkey); } + var icon = CreateNodeIcon(node.RectTransform, order.SymbolSprite, order.Color, + tooltip: hasOptions || characterContext != null ? order.Name : order.Name + + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); + + if (!canSomeoneHearCharacter) + { + node.CanBeFocused = icon.CanBeFocused = false; + CreateBlockIcon(node.RectTransform); + } + else if (hotkey >= 0) + { + CreateHotkeyIcon(node.RectTransform, hotkey); + } return node; } @@ -1839,7 +1902,7 @@ namespace Barotrauma Font = GUI.SmallFont, OnClicked = (_, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!CanIssueOrders) { return false; } var o = userData as Tuple; SetCharacterOrder(characterContext ?? GetBestCharacterForOrder(o.Item1), o.Item1, o.Item2, Character.Controlled); DisableCommandUI(); @@ -1888,20 +1951,31 @@ namespace Barotrauma UserData = new Tuple(order, option), OnClicked = (_, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!CanIssueOrders) { return false; } var o = userData as Tuple; SetCharacterOrder(characterContext ?? GetBestCharacterForOrder(o.Item1), o.Item1, o.Item2, Character.Controlled); DisableCommandUI(); return true; } }; + GUIImage icon = null; if (order.Prefab.OptionSprites.TryGetValue(option, out Sprite sprite)) { - CreateNodeIcon(node.RectTransform, sprite, order.Color, - tooltip: characterContext != null ? optionName : - optionName + "\nLMB: " + TextManager.Get("commandui.quickassigntooltip") + "\nRMB: " + TextManager.Get("commandui.manualassigntooltip")); + icon = CreateNodeIcon(node.RectTransform, sprite, order.Color, + tooltip: characterContext != null ? optionName : optionName + + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.leftmouse") : TextManager.Get("input.rightmouse")) + ": " + TextManager.Get("commandui.quickassigntooltip") + + "\n" + (!PlayerInput.MouseButtonsSwapped() ? TextManager.Get("input.rightmouse") : TextManager.Get("input.leftmouse")) + ": " + TextManager.Get("commandui.manualassigntooltip")); + } + if (!CanSomeoneHearCharacter()) + { + node.CanBeFocused = false; + if (icon != null) { icon.CanBeFocused = false; } + CreateBlockIcon(node.RectTransform); + } + else if (hotkey >= 0) + { + CreateHotkeyIcon(node.RectTransform, hotkey); } - if (hotkey >= 0) { CreateHotkeyIcon(node.RectTransform, hotkey); } return node; } @@ -1990,7 +2064,7 @@ namespace Barotrauma UserData = order, OnClicked = ExpandAssignmentNodes }; - CreateNodeIcon(expandNode.RectTransform, Order.ExpandNode, order.Item1.Color, tooltip: TextManager.Get("commandui.expand")); + CreateNodeIcon(expandNode.RectTransform, "CommandExpandNode", order.Item1.Color, tooltip: TextManager.Get("commandui.expand")); hotkey = optionNodes.Count + 1; CreateHotkeyIcon(expandNode.RectTransform, hotkey % 10); @@ -2029,7 +2103,7 @@ namespace Barotrauma { OnClicked = (button, userData) => { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) { return false; } + if (!CanIssueOrders) { return false; } SetCharacterOrder(character, order.Item1, order.Item2, Character.Controlled); DisableCommandUI(); return true; @@ -2039,7 +2113,7 @@ namespace Barotrauma // Container var icon = new GUIImage( new RectTransform(new Vector2(1.2f), node.RectTransform, anchor: Anchor.Center), - Order.NodeContainer, + "CommandNodeContainer", scaleToFit: true) { Color = character.Info.Job.Prefab.UIColor * nodeColorMultiplier, @@ -2060,19 +2134,15 @@ namespace Barotrauma }; bool canHear = character.CanHearCharacter(Character.Controlled); - if (!canHear) { node.CanBeFocused = icon.CanBeFocused = false; - new GUIImage(new RectTransform(Vector2.One, node.RectTransform, Anchor.Center), cancelIcon, scaleToFit: true) - { - Color = GUI.Style.Red * nodeColorMultiplier - }; + CreateBlockIcon(node.RectTransform); } - if (canHear && hotkey >= 0) + if (hotkey >= 0) { - CreateHotkeyIcon(node.RectTransform, hotkey); - optionNodes.Add(new Tuple(node, Keys.D0 + hotkey)); + if (canHear) { CreateHotkeyIcon(node.RectTransform, hotkey); } + optionNodes.Add(new Tuple(node, canHear ? Keys.D0 + hotkey : Keys.None)); } else { @@ -2080,10 +2150,10 @@ namespace Barotrauma } } - private void CreateNodeIcon(RectTransform parent, Sprite sprite, Color color, string tooltip = null) + private GUIImage CreateNodeIcon(RectTransform parent, Sprite sprite, Color color, string tooltip = null) { // Icon - new GUIImage( + return new GUIImage( new RectTransform(Vector2.One, parent), sprite, scaleToFit: true) @@ -2097,11 +2167,33 @@ namespace Barotrauma }; } + private void CreateNodeIcon(RectTransform parent, string style, Color? color = null, string tooltip = null) + { + // Icon + var icon = new GUIImage( + new RectTransform(Vector2.One, parent), + style, + scaleToFit: true) + { + ToolTip = tooltip, + UserData = "colorsource" + }; + if (color.HasValue) + { + icon.Color = color.Value * nodeColorMultiplier; + icon.HoverColor = color.Value; + } + else + { + icon.Color = icon.HoverColor * nodeColorMultiplier; + } + } + private void CreateHotkeyIcon(RectTransform parent, int hotkey, bool enlargeIcon = false) { var bg = new GUIImage( new RectTransform(new Vector2(enlargeIcon ? 0.4f : 0.25f), parent, anchor: Anchor.BottomCenter, pivot: Pivot.Center), - Order.HotkeyContainer, + "CommandHotkeyContainer", scaleToFit: true) { CanBeFocused = false, @@ -2117,6 +2209,15 @@ namespace Barotrauma }; } + private void CreateBlockIcon(RectTransform parent) + { + new GUIImage(new RectTransform(Vector2.One, parent, anchor: Anchor.Center), cancelIcon, scaleToFit: true) + { + Color = GUI.Style.Red * nodeColorMultiplier, + HoverColor = GUI.Style.Red + }; + } + private int GetCircumferencePointCount(int nodes) { return nodes % 2 > 0 ? nodes : nodes + 1; @@ -2134,7 +2235,7 @@ namespace Barotrauma else if (shortcutCenterNode != null) { bearing = GetBearing( - centerNode.RectTransform.AbsoluteOffset.ToVector2(), + centerNode.RectTransform.AnimTargetPos.ToVector2(), shorcutCenterNodeOffset.ToVector2()); } return nodeCount % 2 > 0 ? @@ -2155,7 +2256,8 @@ namespace Barotrauma private Character GetBestCharacterForOrder(Order order) { - return characters.FindAll(c => c != Character.Controlled) + if (Character.Controlled == null) { return null; } + return characters.FindAll(c => c != Character.Controlled && c.TeamID == Character.Controlled.TeamID) .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .ThenByDescending(c => order.HasAppropriateJob(c)) .ThenBy(c => c.CurrentOrder?.Weight) @@ -2164,15 +2266,17 @@ namespace Barotrauma private List GetCharactersSortedForOrder(Order order) { + if (Character.Controlled == null) { return new List(); } if (order.Identifier == "follow") { - return characters.FindAll(c => c != Character.Controlled) + return characters.FindAll(c => c != Character.Controlled && c.TeamID == Character.Controlled.TeamID) .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .ToList(); } else { - return characters.OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) + return characters.FindAll(c => c.TeamID == Character.Controlled.TeamID) + .OrderByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier) .ThenByDescending(c => order.HasAppropriateJob(c)) .ThenBy(c => c.CurrentOrder?.Weight) .ToList(); @@ -2285,18 +2389,14 @@ namespace Barotrauma if (canIssueOrders) { //report buttons are hidden when accessing another character's inventory - ReportButtonFrame.Visible = - Character.Controlled?.SelectedCharacter?.Inventory == null || - !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed; + ReportButtonFrame.Visible = !Character.Controlled.ShouldLockHud() && + (Character.Controlled?.SelectedCharacter?.Inventory == null || + !Character.Controlled.SelectedCharacter.CanInventoryBeAccessed); var reportButtonParent = ChatBox ?? GameMain.Client?.ChatBox; if (reportButtonParent == null) { return; } - /*reportButtonFrame.RectTransform.AbsoluteOffset = new Point( - Math.Min(reportButtonParent.GUIFrame.Rect.X, reportButtonParent.ToggleButton.Rect.X) - reportButtonFrame.Rect.Width - (int)(10 * GUI.Scale), - reportButtonParent.GUIFrame.Rect.Y);*/ - - ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(reportButtonParent.GUIFrame.Rect.Right + (int)(10 * GUI.Scale), reportButtonParent.GUIFrame.Rect.Y); + ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(reportButtonParent.GUIFrame.Rect.Right + (int)(10 * GUI.Scale), reportButtonParent.GUIFrame.Rect.Y - reportButtonParent.ToggleButton.Rect.Height); bool hasFires = Character.Controlled.CurrentHull.FireSources.Count > 0; ToggleReportButton("reportfire", hasFires); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/MechanicTutorial.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/MechanicTutorial.cs index ba5551d9d..4b898fd5c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/MechanicTutorial.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/MechanicTutorial.cs @@ -348,6 +348,9 @@ namespace Barotrauma.Tutorials TriggerTutorialSegment(4); // Deconstruct SetHighlight(mechanic_craftingCabinet.Item, true); + + bool gotOxygenTank = false; + bool gotSodium = false; do { if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item) @@ -381,8 +384,18 @@ namespace Barotrauma.Tutorials } } } + + if (!gotOxygenTank && mechanic.Inventory.FindItemByIdentifier("oxygentank") != null) + { + gotOxygenTank = true; + } + if (!gotSodium && mechanic.Inventory.FindItemByIdentifier("sodium") != null) + { + gotSodium = true; + } yield return null; - } while ((mechanic.Inventory.FindItemByIdentifier("oxygentank") == null && mechanic.Inventory.FindItemByIdentifier("aluminium") == null) || mechanic.Inventory.FindItemByIdentifier("sodium") == null); // Wait until looted + } while (!gotOxygenTank || !gotSodium); // Wait until looted + yield return new WaitForSeconds(1.0f, false); SetHighlight(mechanic_craftingCabinet.Item, false); SetHighlight(mechanic_deconstructor.Item, true); @@ -424,7 +437,9 @@ namespace Barotrauma.Tutorials } } yield return null; - } while (mechanic.Inventory.FindItemByIdentifier("aluminium") == null); // Wait until deconstructed + } while ( + mechanic.Inventory.FindItemByIdentifier("aluminium") == null && + mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium") == null); // Wait until aluminium obtained SetHighlight(mechanic_deconstructor.Item, false); RemoveCompletedObjective(segments[4]); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs index 364fdab56..8c16da963 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs @@ -403,20 +403,20 @@ namespace Barotrauma UnsavedSettings = true; var msgBox = new GUIMessageBox( - TextManager.Get("RestartRequiredLabel"), - TextManager.Get("RestartRequiredLanguage"), - buttons: new string[] { TextManager.Get("Cancel"), TextManager.Get("OK") }); + TextManager.Get("RestartRequiredLabel"), + TextManager.Get("RestartRequiredLanguage"), + buttons: new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") }); msgBox.Buttons[0].OnClicked += (btn, userdata) => + { + ApplySettings(); + GameMain.Instance.Exit(); + return true; + }; msgBox.Buttons[1].OnClicked += (btn, userdata) => { Language = prevLanguage; languageDD.SelectItem(Language); msgBox.Close(); return true; - }; msgBox.Buttons[1].OnClicked += (btn, userdata) => - { - ApplySettings(); - GameMain.Instance.Exit(); - return true; }; return true; @@ -1057,7 +1057,7 @@ namespace Barotrauma { UserData = i }; - keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, keyBox.Rect.Width); + keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z)); keyBox.OnSelected += KeyBoxSelected; keyBox.SelectedColor = Color.Gold * 0.3f; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs index 768553baa..23f5d6ae5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs @@ -11,7 +11,7 @@ using System.Xml.Linq; namespace Barotrauma { partial class CharacterInventory : Inventory - { + { public enum Layout { Default, @@ -35,20 +35,13 @@ namespace Barotrauma } private static Dictionary limbSlotIcons; - private static Sprite inventoryBackgroundSprite, inventoryExtendButton, inventoryExtendUpArrow, inventoryExtendDownArrow; - - public Rectangle InventoryToggleArea; - public bool InventoryToggleContains = false; - private Vector2 inventoryExtendButtonOffset, inventoryArrowOffset; - private int inventoryOpeningOffset; - - private Point prevResolution; public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; - public Vector2[] SlotPositions; - private Vector2 bgScale; + private Point screenResolution; + public Vector2[] SlotPositions; + private Layout layout; public Layout CurrentLayout { @@ -57,7 +50,7 @@ namespace Barotrauma { if (layout == value) return; layout = value; - SetSlotPositions(); + SetSlotPositions(layout); } } public bool Hidden { get; set; } @@ -66,8 +59,6 @@ namespace Barotrauma private float hidePersonalSlotsState; private GUIButton hideButton; private Rectangle personalSlotArea; - private bool inventoryOpen = false; - private bool wasInventoryToggledAutomatically = false; public bool HidePersonalSlots { @@ -112,16 +103,10 @@ namespace Barotrauma limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128))); limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128))); limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2))); - - inventoryBackgroundSprite = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(252, 317, 263, 197)); - inventoryExtendButton = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(533, 300, 96, 19)); - inventoryExtendUpArrow = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(640, 310, 10, 10)); - inventoryExtendDownArrow = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(668, 310, 10, 10)); } - SlotPositions = new Vector2[SlotTypes.Length]; CurrentLayout = Layout.Default; - SetSlotPositions(); + SetSlotPositions(layout); } protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex) @@ -157,6 +142,8 @@ namespace Barotrauma public override void CreateSlots() { if (slots == null) { slots = new InventorySlot[capacity]; } + + float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f; for (int i = 0; i < capacity; i++) { @@ -166,7 +153,7 @@ namespace Barotrauma Rectangle slotRect = new Rectangle( (int)(SlotPositions[i].X), (int)(SlotPositions[i].Y), - (int)(slotSprite.size.X * UIScale), (int)(slotSprite.size.Y * UIScale)); + (int)(slotSprite.size.X * multiplier), (int)(slotSprite.size.Y * multiplier)); if (Items[i] != null) { @@ -208,54 +195,31 @@ namespace Barotrauma } } + screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); CalculateBackgroundFrame(); } protected override void CalculateBackgroundFrame() { Rectangle frame = Rectangle.Empty; - int firstSlotLocationY = 0; - int lastSlotLocationY = 0; - for (int i = 0; i < capacity; i++) { if (HideSlot(i)) continue; - if (PersonalSlots.HasFlag(SlotTypes[i])) continue; - if (IsHandSlot(SlotTypes[i])) continue; if (frame == Rectangle.Empty) { - firstSlotLocationY = slots[i].Rect.Location.Y; frame = slots[i].Rect; continue; } frame = Rectangle.Union(frame, slots[i].Rect); - lastSlotLocationY = slots[i].Rect.Location.Y; } - - frame.Inflate(25f * UIScale, 25f * UIScale); - - if (layout == Layout.Default) - { - inventoryOpeningOffset = firstSlotLocationY - lastSlotLocationY; - - bgScale = new Vector2((float)frame.Width / (float)inventoryBackgroundSprite.SourceRect.Width, (float)frame.Height / (float)inventoryBackgroundSprite.SourceRect.Height); - - inventoryExtendButtonOffset = new Vector2(inventoryExtendButton.size.X * UIScale * 1.5f / 2f, inventoryExtendButton.size.Y * UIScale * 1.5f / 2f + UIScale * 6); - inventoryArrowOffset = new Vector2(inventoryExtendUpArrow.size.X * UIScale * 1.25f / 2f, inventoryExtendUpArrow.size.Y * UIScale * 1.25f / 2f); - InventoryToggleArea = new Rectangle(new Point(frame.Center.X, frame.Top) - inventoryExtendButtonOffset.ToPoint(), inventoryExtendButton.size.ToPoint() + new Point(0, (int)(UIScale * 6f))); - if (!inventoryOpen) - { - frame.Offset(0, inventoryOpeningOffset); - InventoryToggleArea.Offset(0, inventoryOpeningOffset); - } - } - + frame.Inflate(10, 30); + frame.Location -= new Point(0, 25); BackgroundFrame = frame; } protected override bool HideSlot(int i) { - if (slots[i].Disabled) return true; + if (slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null)) return true; if (layout == Layout.Default) { @@ -268,23 +232,34 @@ namespace Barotrauma return true; } + //don't show the equip slot if the item is also in the default inventory + if (SlotTypes[i] != InvSlotType.Any && Items[i] != null) + { + for (int j = 0; j < capacity; j++) + { + if (SlotTypes[j] == InvSlotType.Any && Items[j] == Items[i]) return true; + } + } + return false; } - - protected override bool IsSlotHiddenDueToToggleState(int i) + private void SetSlotPositions(Layout layout) { - return layout == Layout.Default && !inventoryOpen && slots[i].QuickUseKey == Keys.None && SlotTypes[i] == InvSlotType.Any; - } + int spacing; - private void SetSlotPositions() - { - Layout layout = CurrentLayout; - int spacing = (int)(10 * UIScale); - Point slotSize = (SlotSpriteSmall.size * UIScale).ToPoint(); + bool isFourByThree = GUI.IsFourByThree(); + if (isFourByThree) + { + spacing = (int)(5 * UIScale); + } + else + { + spacing = (int)(10 * UIScale); + } + + Point slotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint(); int bottomOffset = slotSize.Y + spacing * 2 + ContainedIndicatorHeight; - prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); - if (slots == null) { CreateSlots(); } hideButton.Visible = false; @@ -296,47 +271,27 @@ namespace Barotrauma int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s)); int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s)); - int firstRowSlotCount = hotkeyCount > normalSlotCount ? normalSlotCount : hotkeyCount; - - int startX = GameMain.GraphicsWidth / 2 - firstRowSlotCount * (slotSize.X + spacing) / 2; - int x = startX; - int equipmentX = GameMain.GraphicsWidth - slotSize.X * 2; - int buttonIndex = 0; - int startY = GameMain.GraphicsHeight - bottomOffset; - int y = startY; - int handIndex = 1; + int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (slotSize.X + spacing) / 2; + int upperX = GameMain.GraphicsWidth - slotSize.X * 2; //make sure the rightmost normal slot doesn't overlap with the personal slots - x -= Math.Max(x + firstRowSlotCount * (slotSize.X + spacing) - (equipmentX - personalSlotCount * (slotSize.X + spacing)), 0); + x -= Math.Max((x + normalSlotCount * (slotSize.X + spacing)) - (upperX - personalSlotCount * (slotSize.X + spacing)), 0); int hideButtonSlotIndex = -1; for (int i = 0; i < SlotPositions.Length; i++) { if (PersonalSlots.HasFlag(SlotTypes[i])) { - SlotPositions[i] = new Vector2(equipmentX, startY); - equipmentX -= slotSize.X + spacing; + SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset); + upperX -= slotSize.X + spacing; personalSlotArea = (hideButtonSlotIndex == -1) ? new Rectangle(SlotPositions[i].ToPoint(), slotSize) : Rectangle.Union(personalSlotArea, new Rectangle(SlotPositions[i].ToPoint(), slotSize)); hideButtonSlotIndex = i; } - else if (IsHandSlot(SlotTypes[i])) - { - SlotPositions[i] = new Vector2(startX - (slotSize.X + spacing * 4) - (slotSize.X + spacing) * handIndex, startY); - handIndex--; - } else { - if (buttonIndex >= hotkeyCount) - { - y -= bottomOffset; - buttonIndex = 0; - x = startX; - } - - buttonIndex++; - SlotPositions[i] = new Vector2(x, y); + SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); x += slotSize.X + spacing; } } @@ -354,6 +309,7 @@ namespace Barotrauma break; case Layout.Right: { + int extraOffset = 0; int x = HUDLayoutSettings.InventoryAreaLower.Right; int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - slotSize.X - spacing; for (int i = 0; i < slots.Length; i++) @@ -364,35 +320,24 @@ namespace Barotrauma //upperX -= slotSize.X + spacing; } else - { - if (i < slots.Length - 5) - { - x -= slotSize.X + spacing; - } + { + x -= slotSize.X + spacing; } } int lowerX = x; - int y = GameMain.GraphicsHeight - bottomOffset; - for (int i = 0; i < SlotPositions.Length; i++) { if (HideSlot(i)) continue; if (PersonalSlots.HasFlag(SlotTypes[i])) { - SlotPositions[i] = new Vector2(personalSlotX, y - bottomOffset); + SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - spacing * 2); personalSlotX -= slots[i].Rect.Width + spacing; } else { - SlotPositions[i] = new Vector2(x, y); + SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); x += slots[i].Rect.Width + spacing; - - if (i == SlotPositions.Length - 6) - { - x = lowerX + (slots[i].Rect.Width + spacing) * 2; - y -= bottomOffset; - } } } @@ -401,37 +346,28 @@ namespace Barotrauma { if (!HideSlot(i)) continue; x -= slots[i].Rect.Width + spacing; - SlotPositions[i] = new Vector2(x, y); + SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset - extraOffset); } } break; case Layout.Left: { - int x = HUDLayoutSettings.InventoryAreaLower.Left; - int y = GameMain.GraphicsHeight - bottomOffset; + int x = HUDLayoutSettings.InventoryAreaLower.X; int personalSlotX = x; - for (int i = 0; i < SlotPositions.Length; i++) { if (HideSlot(i)) continue; if (PersonalSlots.HasFlag(SlotTypes[i])) { - SlotPositions[i] = new Vector2(personalSlotX, y - bottomOffset); + SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2); personalSlotX += slots[i].Rect.Width + spacing; } else { - SlotPositions[i] = new Vector2(x, y); + SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset); x += slots[i].Rect.Width + spacing; - - if (i == SlotPositions.Length - 7) - { - x -= (slots[i].Rect.Width + spacing) * 6; - y -= bottomOffset; - } } } - for (int i = 0; i < SlotPositions.Length; i++) { if (!HideSlot(i)) continue; @@ -483,11 +419,7 @@ namespace Barotrauma { HUDLayoutSettings.InventoryTopY = slots[0].EquipButtonRect.Y - (int)(15 * GUI.Scale); } - } - - private bool IsHandSlot(InvSlotType slotType) - { - return slotType == InvSlotType.LeftHand || slotType == InvSlotType.RightHand; + } protected override void ControlInput(Camera cam) @@ -500,32 +432,6 @@ namespace Barotrauma } } - public void ToggleInventory(bool automaticToggle = false) - { - if (Character.Controlled == null || !Character.Controlled.IsHuman || wasInventoryToggledAutomatically || inventoryOpen && automaticToggle) return; - inventoryOpen = !inventoryOpen; - if (inventoryOpen) - { - wasInventoryToggledAutomatically = automaticToggle; - BackgroundFrame.Offset(0, -inventoryOpeningOffset); - InventoryToggleArea.Offset(0, -inventoryOpeningOffset); - } - else - { - BackgroundFrame.Offset(0, inventoryOpeningOffset); - InventoryToggleArea.Offset(0, inventoryOpeningOffset); - } - } - - private void HandleAutomaticInventoryState() - { - if (wasInventoryToggledAutomatically && inventoryOpen && Character.Controlled.SelectedConstruction == null && Character.Controlled.SelectedCharacter == null) - { - wasInventoryToggledAutomatically = false; - ToggleInventory(); - } - } - public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) { if (!AccessibleWhenAlive && !character.IsDead) @@ -534,8 +440,6 @@ namespace Barotrauma return; } - HandleAutomaticInventoryState(); - base.Update(deltaTime, cam); bool hoverOnInventory = GUI.MouseOn == null && @@ -564,12 +468,6 @@ namespace Barotrauma slots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState); } } - - InventoryToggleContains = InventoryToggleArea.Contains(PlayerInput.MousePosition); - if (InventoryToggleContains && PlayerInput.PrimaryMouseButtonClicked()) - { - ToggleInventory(); - } } if (hoverOnInventory) { HideTimer = 0.5f; } @@ -726,10 +624,7 @@ namespace Barotrauma private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime) { - Rectangle modifiedRect = slot.EquipButtonRect; - modifiedRect.Width = slot.InteractRect.Width; - - slot.EquipButtonState = modifiedRect.Contains(PlayerInput.MousePosition) ? + slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ? GUIComponent.ComponentState.Hover : GUIComponent.ComponentState.None; if (PlayerInput.LeftButtonHeld() && PlayerInput.RightButtonHeld()) { @@ -803,8 +698,6 @@ namespace Barotrauma continue; } - if (num > hotkeyCount) break; - if (SlotTypes[i] == InvSlotType.Any) { slots[i].QuickUseKey = Keys.D0 + num % 10; @@ -950,7 +843,7 @@ namespace Barotrauma if (character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) { //player has selected the inventory of another character -> attempt to move the item there - success = character.SelectedCharacter.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true, true); + success = character.SelectedCharacter.Inventory.TryPutItem(item, Character.Controlled, item.AllowedSlots, true); } break; case QuickUseAction.PutToContainer: @@ -966,7 +859,7 @@ namespace Barotrauma character.SelectedBy.Inventory != null) { //item is in the inventory of another character -> attempt to get the item from there - success = character.SelectedBy.Inventory.TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true, true); + success = character.SelectedBy.Inventory.TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true); } break; case QuickUseAction.TakeFromContainer: @@ -985,7 +878,7 @@ namespace Barotrauma // No subinventory found or placing unsuccessful -> attempt to put in the character's inventory if (!success) { - success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true, true); + success = TryPutItemWithAutoEquipCheck(item, Character.Controlled, item.AllowedSlots, true); } break; case QuickUseAction.PutToEquippedItem: @@ -1017,86 +910,128 @@ namespace Barotrauma GUI.PlayUISound(success ? GUISoundType.PickItem : GUISoundType.PickItemFail); } - public void DrawThis(SpriteBatch spriteBatch) + public void DrawOwn(SpriteBatch spriteBatch) { - if (!AccessibleWhenAlive && !character.IsDead) { return; } - if (slots == null) { CreateSlots(); } - - if (prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight) + if (!AccessibleWhenAlive && !character.IsDead) return; + if (slots == null) CreateSlots(); + if (GameMain.GraphicsWidth != screenResolution.X || + GameMain.GraphicsHeight != screenResolution.Y || + prevUIScale != UIScale || + prevHUDScale != GUI.Scale) { - SetSlotPositions(); + SetSlotPositions(layout); + prevUIScale = UIScale; + prevHUDScale = GUI.Scale; + } + + if (layout == Layout.Center) + { + CalculateBackgroundFrame(); + GUI.DrawRectangle(spriteBatch, BackgroundFrame, Color.Black * 0.8f, true); + GUI.DrawString(spriteBatch, + new Vector2((int)(BackgroundFrame.Center.X - GUI.Font.MeasureString(character.Name).X / 2), (int)BackgroundFrame.Y + 5), + character.Name, Color.White * 0.9f); + } + + for (int i = 0; i < capacity; i++) + { + if (HideSlot(i)) continue; + + Rectangle interactRect = slots[i].InteractRect; + interactRect.Location += slots[i].DrawOffset.ToPoint(); + + //don't draw the item if it's being dragged out of the slot + bool drawItem = draggingItem == null || draggingItem != Items[i] || interactRect.Contains(PlayerInput.MousePosition); + DrawSlot(spriteBatch, this, slots[i], Items[i], i, drawItem, SlotTypes[i]); } if (hideButton != null && hideButton.Visible && !Locked) { hideButton.DrawManually(spriteBatch, alsoChildren: true); } - - if (layout == Layout.Default) - { - inventoryBackgroundSprite.Draw(spriteBatch, BackgroundFrame.Location.ToVector2(), Color.White, Vector2.Zero, rotate: 0, scale: bgScale); - - Vector2 backgroundFrameCenter = new Vector2(BackgroundFrame.Center.X, BackgroundFrame.Top); - inventoryExtendButton.Draw(spriteBatch, backgroundFrameCenter - inventoryExtendButtonOffset, Color.White, scale: UIScale * 1.5f); - - Vector2 arrowPosition = backgroundFrameCenter - inventoryArrowOffset; - if (inventoryOpen) - { - inventoryExtendDownArrow.Draw(spriteBatch, arrowPosition, Color.White, scale: UIScale * 1.25f); - } - else - { - inventoryExtendUpArrow.Draw(spriteBatch, arrowPosition, Color.White, scale: UIScale * 1.25f); - } - - GUI.DrawString(spriteBatch, arrowPosition + new Vector2(UIScale * 25, -3 * UIScale), GameMain.Config.KeyBindText(InputType.ToggleInventory), Color.White, font: GUI.HotkeyFont); - } InventorySlot highlightedQuickUseSlot = null; for (int i = 0; i < capacity; i++) { if (HideSlot(i)) continue; - if (IsSlotHiddenDueToToggleState(i)) continue; - InventorySlot slot = slots[i]; - - Item item = Items[i]; - InvSlotType slotType = SlotTypes[i]; - - Rectangle interactRect = slot.InteractRect; - interactRect.Location += slot.DrawOffset.ToPoint(); - - //don't draw the item if it's being dragged out of the slot - bool drawItem = draggingItem == null || draggingItem != item || interactRect.Contains(PlayerInput.MousePosition); - DrawSlot(spriteBatch, this, slot, item, i, drawItem, slotType); - - if (item == null || (draggingItem == item && !slot.InteractRect.Contains(PlayerInput.MousePosition)) || !item.AllowedSlots.Any(a => a != InvSlotType.Any)) + if (Items[i] == null || + (draggingItem == Items[i] && !slots[i].InteractRect.Contains(PlayerInput.MousePosition)) || + !Items[i].AllowedSlots.Any(a => a != InvSlotType.Any)) { //draw limb icons on empty slots - if (limbSlotIcons.ContainsKey(slotType)) + if (limbSlotIcons.ContainsKey(SlotTypes[i])) { - var icon = limbSlotIcons[slotType]; - icon.Draw(spriteBatch, slot.Rect.Center.ToVector2() + slot.DrawOffset, GUIColorSettings.EquipmentSlotIconColor, origin: icon.size / 2, scale: slot.Rect.Width / icon.size.X); + var icon = limbSlotIcons[SlotTypes[i]]; + icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2() + slots[i].DrawOffset, GUIColorSettings.EquipmentSlotIconColor, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X); } + continue; + } + if (draggingItem == Items[i] && !slots[i].IsHighlighted) continue; + + //draw hand icons if the item is equipped in a hand slot + if (IsInLimbSlot(Items[i], InvSlotType.LeftHand)) + { + var icon = limbSlotIcons[InvSlotType.LeftHand]; + icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f); + } + if (IsInLimbSlot(Items[i], InvSlotType.RightHand)) + { + var icon = limbSlotIcons[InvSlotType.RightHand]; + icon.Draw(spriteBatch, new Vector2(slots[i].Rect.Right, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f); + } - // Draw always on hotkeys - if (slot.QuickUseKey != Keys.None) - { - DrawIndicatorAndHotkey(spriteBatch, slot, slot.EquipButtonState == GUIComponent.ComponentState.Hover); - } + GUIComponent.ComponentState state = slots[i].EquipButtonState; + if (state == GUIComponent.ComponentState.Hover) + { + highlightedQuickUseSlot = slots[i]; + } + if (!Items[i].AllowedSlots.Any(a => a == InvSlotType.Any)) + { continue; } - if (draggingItem == item && !slot.IsHighlighted) continue; - - bool hover = slot.EquipButtonState == GUIComponent.ComponentState.Hover; - if (hover) highlightedQuickUseSlot = slot; - - if (!item.AllowedSlots.Any(a => a == InvSlotType.Any)) continue; - - DrawIndicatorAndHotkey(spriteBatch, slot, hover); + Color color = Color.White; + if (Locked) + { + color *= 0.5f; + } + if (character.HasEquippedItem(Items[i])) + { + switch (state) + { + case GUIComponent.ComponentState.None: + EquippedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + case GUIComponent.ComponentState.Hover: + EquippedHoverIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + case GUIComponent.ComponentState.Pressed: + case GUIComponent.ComponentState.Selected: + case GUIComponent.ComponentState.HoverSelected: + EquippedClickedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + } + } + else + { + switch (state) + { + case GUIComponent.ComponentState.None: + UnequippedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + case GUIComponent.ComponentState.Hover: + UnequippedHoverIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + case GUIComponent.ComponentState.Pressed: + case GUIComponent.ComponentState.Selected: + case GUIComponent.ComponentState.HoverSelected: + UnequippedClickedIndicator.Draw(spriteBatch, slots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, UIScale * IndicatorScaleAdjustment); + break; + } + } } if (highlightedQuickUseSlot != null && !string.IsNullOrEmpty(highlightedQuickUseSlot.QuickUseButtonToolTip)) @@ -1104,30 +1039,5 @@ namespace Barotrauma GUIComponent.DrawToolTip(spriteBatch, highlightedQuickUseSlot.QuickUseButtonToolTip, highlightedQuickUseSlot.EquipButtonRect); } } - - private void DrawIndicatorAndHotkey(SpriteBatch spriteBatch, InventorySlot slot, bool hover) - { - - Color indicatorColor = (hover) ? Color.White : Color.White * 0.8f; - - /*if (character.HasEquippedItem(item)) - { - indicatorColor = hover ? GUIColorSettings.InventorySlotEquippedColor : GUIColorSettings.InventorySlotEquippedColor * 0.8f; - } - else - { - indicatorColor = hover ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f; - }*/ - - if (Locked) indicatorColor *= 0.3f; - - Rectangle equipRect = slot.EquipButtonRect; - EquipIndicator.Draw(spriteBatch, new Vector2(equipRect.Center.X, equipRect.Bottom), indicatorColor, EquipIndicator.Origin, 0, UIScale * 0.6f); - - if (slot.QuickUseKey != Keys.None) - { - GUI.DrawString(spriteBatch, new Vector2(slot.Rect.Center.X - 2, slot.Rect.Top), slot.QuickUseKey.ToString().Substring(1, 1), Color.Black, font: GUI.HotkeyFont); - } - } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs index 01c826c8c..ca1267f9b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs @@ -334,9 +334,9 @@ namespace Barotrauma.Items.Components //docking interface ---------------------------------------------------- float dockingButtonSize = 1.1f; float elementScale = 0.6f; - dockingContainer = new GUIFrame(new RectTransform(new Point(160).Multiply(GUI.Scale * dockingButtonSize), GuiFrame.RectTransform, Anchor.BottomLeft) + dockingContainer = new GUIFrame(new RectTransform(Sonar.controlBoxSize, GuiFrame.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.Smallest) { - RelativeOffset = new Vector2(Sonar.controlBoxOffset.X + 0.15f, -0.1f) + RelativeOffset = Sonar.controlBoxOffset }, style: null); dockText = TextManager.Get("label.navterminaldock", fallBackTag: "captain.dock"); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Connection.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Connection.cs index fe7f34d2b..01cbec3df 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Connection.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Connection.cs @@ -156,6 +156,10 @@ namespace Barotrauma.Items.Components { panel.DisconnectedWires.Add(DraggingConnected); } + else if (DraggingConnected.Connections[0] == null && DraggingConnected.Connections[1] == null) + { + DraggingConnected.ClearConnections(user: Character.Controlled); + } } } @@ -187,7 +191,7 @@ namespace Barotrauma.Items.Components x = (int)(x + width / 2 - step * (panel.DisconnectedWires.Count() - 1) / 2); foreach (Wire wire in panel.DisconnectedWires) { - if (wire == DraggingConnected) { continue; } + if (wire == DraggingConnected && mouseInRect) { continue; } Connection recipient = wire.OtherConnection(null); string label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})"; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/CustomInterface.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/CustomInterface.cs index a99381f5a..be8ef5baf 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/CustomInterface.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/CustomInterface.cs @@ -109,17 +109,20 @@ namespace Barotrauma.Items.Components { base.CreateEditingHUD(editor); - PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(customInterfaceElementList[0]); - PropertyDescriptor labelProperty = properties.Find("Label", false); - PropertyDescriptor signalProperty = properties.Find("Signal", false); - for (int i = 0; i < customInterfaceElementList.Count; i++) - { - editor.CreateStringField(customInterfaceElementList[i], - new SerializableProperty(labelProperty), - customInterfaceElementList[i].Label, "Label #" + (i + 1), ""); - editor.CreateStringField(customInterfaceElementList[i], - new SerializableProperty(signalProperty), - customInterfaceElementList[i].Signal, "Signal #" + (i + 1), ""); + if (customInterfaceElementList.Count > 0) + { + PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(customInterfaceElementList[0]); + PropertyDescriptor labelProperty = properties.Find("Label", false); + PropertyDescriptor signalProperty = properties.Find("Signal", false); + for (int i = 0; i < customInterfaceElementList.Count; i++) + { + editor.CreateStringField(customInterfaceElementList[i], + new SerializableProperty(labelProperty), + customInterfaceElementList[i].Label, "Label #" + (i + 1), ""); + editor.CreateStringField(customInterfaceElementList[i], + new SerializableProperty(signalProperty), + customInterfaceElementList[i].Signal, "Signal #" + (i + 1), ""); + } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs index ac65c1819..ba394badc 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Turret.cs @@ -444,9 +444,10 @@ namespace Barotrauma.Items.Components } powerIndicator.DrawManually(spriteBatch, true); - int requiredChargeIndicatorPos = (int)((powerConsumption / (batteryCapacity * 3600.0f)) * powerIndicator.Rect.Width); + Rectangle sliderRect = powerIndicator.GetSliderRect(1.0f); + int requiredChargeIndicatorPos = (int)(powerConsumption / (batteryCapacity * 3600.0f) * sliderRect.Width); GUI.DrawRectangle(spriteBatch, - new Rectangle(powerIndicator.Rect.X + requiredChargeIndicatorPos, powerIndicator.Rect.Y, 3, powerIndicator.Rect.Height), + new Rectangle(sliderRect.X + requiredChargeIndicatorPos, sliderRect.Y, 2, sliderRect.Height), Color.White * 0.5f, true); } @@ -459,7 +460,7 @@ namespace Barotrauma.Items.Components Point invSlotPos = new Point(GameMain.GraphicsWidth / 2 - totalWidth / 2, (int)(60 * GUI.Scale)); for (int i = 0; i < availableAmmo.Count; i++) { - // TODO: Optimize? Creates multiple new classes per frame? + // TODO: Optimize? Creates multiple new objects per frame? Inventory.DrawSlot(spriteBatch, null, new InventorySlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing)), slotSize)), availableAmmo[i], -1, true); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index 6b2a1fb11..c1bfced8a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -53,10 +53,12 @@ namespace Barotrauma { int buttonDir = Math.Sign(SubInventoryDir); - Vector2 equipIndicatorPos = new Vector2(Rect.Left, Rect.Top); + float sizeY = Inventory.UnequippedIndicator.size.Y * Inventory.UIScale * Inventory.IndicatorScaleAdjustment; + + Vector2 equipIndicatorPos = new Vector2(Rect.Left, Rect.Center.Y + (Rect.Height / 2 + 25 * Inventory.UIScale) * buttonDir - sizeY / 2f); equipIndicatorPos += DrawOffset; - return new Rectangle((int)equipIndicatorPos.X, (int)equipIndicatorPos.Y, Rect.Width, (int)(Inventory.EquipIndicator.size.Y * Inventory.UIScale)); + return new Rectangle((int)equipIndicatorPos.X, (int)equipIndicatorPos.Y, (int)Rect.Width, (int)sizeY); } } @@ -136,19 +138,27 @@ namespace Barotrauma { if (slotSpriteSmall == null) { - //TODO: define these in xml - slotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(12, 9, 115, 115), null, 0); - slotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.682f, SlotSpriteSmall.SourceRect.Height * 0.682f); + //TODO: define this in xml + slotSpriteSmall = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(10, 6, 119, 120), null, 0); + // Adjustment to match the old size of 75,71 + SlotSpriteSmall.size = new Vector2(SlotSpriteSmall.SourceRect.Width * 0.625f, SlotSpriteSmall.SourceRect.Height * 0.625f); } return slotSpriteSmall; } } public static Sprite DraggableIndicator; - public static Sprite EquipIndicator; - + public static Sprite UnequippedIndicator, UnequippedHoverIndicator, UnequippedClickedIndicator, EquippedIndicator, EquippedHoverIndicator, EquippedClickedIndicator; + public static float IndicatorScaleAdjustment + { + get + { + return !GUI.IsFourByThree() ? 0.85f : 0.75f; + } + } + public static Inventory DraggingInventory; - public Rectangle BackgroundFrame; + public Rectangle BackgroundFrame { get; protected set; } private ushort[] receivedItemIDs; private CoroutineHandle syncItemsCoroutine; @@ -305,7 +315,7 @@ namespace Barotrauma int columns = Math.Min(slotsPerRow, capacity); Vector2 spacing = new Vector2(5.0f * UIScale); - spacing.Y += (this is CharacterInventory) ? EquipIndicator.size.Y : ContainedIndicatorHeight; + spacing.Y += (this is CharacterInventory) ? UnequippedIndicator.size.Y * UIScale : ContainedIndicatorHeight; Vector2 rectSize = new Vector2(60.0f * UIScale); padding = new Vector4(spacing.X, spacing.Y, spacing.X, spacing.X); @@ -384,12 +394,7 @@ namespace Barotrauma protected virtual bool HideSlot(int i) { - return slots[i].Disabled; - } - - protected virtual bool IsSlotHiddenDueToToggleState(int i) - { - return false; + return slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null); } public virtual void Update(float deltaTime, Camera cam, bool subInventory = false) @@ -405,7 +410,7 @@ namespace Barotrauma { for (int i = 0; i < capacity; i++) { - if (HideSlot(i) || IsSlotHiddenDueToToggleState(i)) { continue; } + if (HideSlot(i)) { continue; } UpdateSlot(slots[i], i, Items[i], subInventory); } if (!isSubInventory) @@ -557,7 +562,16 @@ namespace Barotrauma var slot = slots[slotIndex]; int dir = slot.SubInventoryDir; Rectangle subRect = slot.Rect; - Vector2 spacing = new Vector2(10 * UIScale, (10 * UIScale + EquipIndicator.size.Y)); + Vector2 spacing; + + if (GUI.IsFourByThree()) + { + spacing = new Vector2(5 * UIScale, (5 + UnequippedIndicator.size.Y) * UIScale); + } + else + { + spacing = new Vector2(10 * UIScale, (10 + UnequippedIndicator.size.Y) * UIScale); + } int columns = (int)Math.Max(Math.Floor(Math.Sqrt(itemCapacity)), 1); while (itemCapacity / columns * (subRect.Height + spacing.Y) > GameMain.GraphicsHeight * 0.5f) @@ -653,24 +667,18 @@ namespace Barotrauma /// Is the mouse on any inventory element (slot, equip button, subinventory...) /// /// - public static bool IsMouseOnInventory(bool ignoreDrag = false) + public static bool IsMouseOnInventory() { if (Character.Controlled == null) return false; - if (!ignoreDrag && draggingItem != null || DraggingInventory != null) return true; + if (draggingItem != null || DraggingInventory != null) return true; if (Character.Controlled.Inventory != null) { var inv = Character.Controlled.Inventory; - - if (inv.BackgroundFrame.Contains(PlayerInput.MousePosition) || inv.InventoryToggleContains) return true; - for (var i = 0; i < inv.slots.Length; i++) { var slot = inv.slots[i]; - - if (inv.HideSlot(i) || inv.IsSlotHiddenDueToToggleState(i)) continue; - if (slot.InteractRect.Contains(PlayerInput.MousePosition)) { return true; @@ -743,11 +751,6 @@ namespace Barotrauma if (inv == null) { return CursorState.Default; } - if (inv.InventoryToggleContains) - { - return CursorState.Hand; - } - foreach (var item in inv.Items) { var container = item?.GetComponent(); @@ -798,12 +801,8 @@ namespace Barotrauma } } - for (int i = 0; i < inv.slots.Length; i++) + foreach (var slot in inv.slots) { - InventorySlot slot = inv.slots[i]; - - if (inv.IsSlotHiddenDueToToggleState(i)) continue; - if (slot.EquipButtonRect.Contains(PlayerInput.MousePosition)) { return CursorState.Hand; @@ -820,7 +819,6 @@ namespace Barotrauma } } } - return CursorState.Default; } @@ -931,20 +929,17 @@ namespace Barotrauma if (selectedSlot == null) { - if (!IsMouseOnInventory(true)) + if (DraggingItemToWorld && + Character.Controlled.FocusedItem?.OwnInventory != null && + Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) && + Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled)) { - if (DraggingItemToWorld && - Character.Controlled.FocusedItem?.OwnInventory != null && - Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) && - Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled)) - { - GUI.PlayUISound(GUISoundType.PickItem); - } - else - { - GUI.PlayUISound(GUISoundType.DropItem); - draggingItem.Drop(Character.Controlled); - } + GUI.PlayUISound(GUISoundType.PickItem); + } + else + { + GUI.PlayUISound(GUISoundType.DropItem); + draggingItem.Drop(Character.Controlled); } } else if (selectedSlot.ParentInventory.Items[selectedSlot.SlotIndex] != draggingItem) @@ -1076,7 +1071,7 @@ namespace Barotrauma bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement; - if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null && !IsMouseOnInventory(true)) + if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null) { var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0]; string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") : @@ -1101,7 +1096,7 @@ namespace Barotrauma } } - if (selectedSlot != null && selectedSlot.Item != null && selectedSlot.Slot.EquipButtonState != GUIComponent.ComponentState.Hover) + if (selectedSlot != null && selectedSlot.Item != null) { Rectangle slotRect = selectedSlot.Slot.Rect; slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint(); @@ -1136,30 +1131,15 @@ namespace Barotrauma { Sprite slotSprite = slot.SlotSprite ?? SlotSpriteSmall; - if (inventory != null && (CharacterInventory.PersonalSlots.HasFlag(type) || (inventory.isSubInventory && (inventory.Owner as Item) != null + /*if (inventory != null && (CharacterInventory.PersonalSlots.HasFlag(type) || (inventory.isSubInventory && (inventory.Owner as Item) != null && (inventory.Owner as Item).AllowedSlots.Any(a => CharacterInventory.PersonalSlots.HasFlag(a))))) { - if (item == null) - { - slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotEmptyColor : GUIColorSettings.EquipmentSlotEmptyColor * 0.8f; - } - else - { - slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotColor : GUIColorSettings.EquipmentSlotColor * 0.8f; - } - + slotColor = slot.IsHighlighted ? GUIColorSettings.EquipmentSlotColor : GUIColorSettings.EquipmentSlotColor * 0.8f; } else { - if (item != null && Character.Controlled != null && Character.Controlled.HasEquippedItem(item)) - { - slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotEquippedColor : GUIColorSettings.InventorySlotEquippedColor * 0.8f; - } - else - { - slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f; - } - } + slotColor = slot.IsHighlighted ? GUIColorSettings.InventorySlotColor : GUIColorSettings.InventorySlotColor * 0.8f; + }*/ if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; } spriteBatch.Draw(slotSprite.Texture, rect, slotSprite.SourceRect, slotColor); @@ -1183,7 +1163,7 @@ namespace Barotrauma canBePut = true; } } - if (slot.MouseOn() && canBePut) + if (slot.MouseOn() && canBePut && selectedSlot?.Slot == slot) { GUI.UIGlow.Draw(spriteBatch, rect, GUI.Style.Green); } @@ -1273,7 +1253,7 @@ namespace Barotrauma if (GameMain.DebugDraw) { GUI.DrawRectangle(spriteBatch, rect, Color.White, false, 0, 1); - GUI.DrawRectangle(spriteBatch, slot.EquipButtonRect, Color.Red, false, 0, 1); + GUI.DrawRectangle(spriteBatch, slot.EquipButtonRect, Color.White, false, 0, 1); } if (slot.HighlightColor != Color.Transparent) @@ -1284,11 +1264,8 @@ namespace Barotrauma if (item != null && drawItem) { Sprite sprite = item.Prefab.InventoryIcon ?? item.Sprite; - - float equipButtonHeightAdjustment = inventory == Character.Controlled?.Inventory ? slot.EquipButtonRect.Height : 0; - - float scale = Math.Min(Math.Min((rect.Width - 10) / sprite.size.X, (rect.Height - 5 - equipButtonHeightAdjustment) / sprite.size.Y), 2.0f); - Vector2 itemPos = rect.Center.ToVector2() + new Vector2(0, equipButtonHeightAdjustment / 2f); + float scale = Math.Min(Math.Min((rect.Width - 10) / sprite.size.X, (rect.Height - 10) / sprite.size.Y), 2.0f); + Vector2 itemPos = rect.Center.ToVector2(); if (itemPos.Y > GameMain.GraphicsHeight) { itemPos.Y -= Math.Min( @@ -1314,6 +1291,15 @@ namespace Barotrauma } sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale); } + + if (inventory != null && + !inventory.Locked && + Character.Controlled?.Inventory == inventory && + slot.QuickUseKey != Keys.None) + { + spriteBatch.Draw(slotHotkeySprite.Texture, rect.ScaleSize(1.15f), slotHotkeySprite.SourceRect, slotColor); + GUI.DrawString(spriteBatch, rect.Location.ToVector2() + new Vector2((int)(4 * UIScale), (int)(-1.25f * UIScale)), slot.QuickUseKey.ToString().Substring(1, 1), Color.Black, font: GUI.SmallFont); + } } public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs index 21fa69741..d4ded9f01 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs @@ -708,6 +708,8 @@ namespace Barotrauma foreach (ItemComponent ic in activeHUDs) { if (ic.GuiFrame == null || ic.AllowUIOverlap || ic.GetLinkUIToComponent() != null) { continue; } + //if the frame covers nearly all of the screen, don't trying to prevent overlaps because it'd fail anyway + if (ic.GuiFrame.Rect.Width >= GameMain.GraphicsWidth * 0.9f && ic.GuiFrame.Rect.Height >= GameMain.GraphicsHeight * 0.9f) { continue; } ic.GuiFrame.RectTransform.ScreenSpaceOffset = Point.Zero; elementsToMove.Add(ic.GuiFrame); debugInitialHudPositions.Add(ic.GuiFrame.Rect); @@ -779,10 +781,10 @@ namespace Barotrauma foreach (ItemComponent ic in activeComponents) { if (ic.HudPriority > 0 && ic.ShouldDrawHUD(character) && - (ic.CanBeSelected || character.HasEquippedItem(this)) && + (ic.CanBeSelected || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped)) && (maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority)) { - if (maxPriorityHUDs.Count > 0 && ic.HudPriority > maxPriorityHUDs[0].HudPriority) maxPriorityHUDs.Clear(); + if (maxPriorityHUDs.Count > 0 && ic.HudPriority > maxPriorityHUDs[0].HudPriority) { maxPriorityHUDs.Clear(); } maxPriorityHUDs.Add(ic); } } @@ -795,7 +797,8 @@ namespace Barotrauma { foreach (ItemComponent ic in activeComponents) { - if ((ic.CanBeSelected || character.HasEquippedItem(this)) && ic.ShouldDrawHUD(character)) + if (ic.ShouldDrawHUD(character) && + (ic.CanBeSelected || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped))) { activeHUDs.Add(ic); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/ItemInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/ItemInventory.cs index 686a91e48..d637d1aa6 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/ItemInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/ItemInventory.cs @@ -11,12 +11,6 @@ namespace Barotrauma { base.ControlInput(cam); cam.OffsetAmount = 0; - - if (Character.Controlled?.Inventory != null) - { - Character.Controlled.Inventory.ToggleInventory(true); - } - //if this is used, we need to implement syncing this inventory with the server /*Character.DisableControls = true; if (Character.Controlled != null) @@ -25,7 +19,7 @@ namespace Barotrauma { Character.Controlled.SelectedConstruction = null; } - }*/ + }*/ } protected override void CalculateBackgroundFrame() diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 5c4a23a96..c5613e0cb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -60,6 +60,8 @@ namespace Barotrauma.Networking private bool connected; + private bool roundInitialized; + private byte myID; private List otherClients; @@ -148,6 +150,8 @@ namespace Barotrauma.Networking this.ownerKey = ownerKey; this.steamP2POwner = steamP2POwner; + roundInitialized = false; + allowReconnect = true; netStats = new NetStats(); @@ -708,6 +712,9 @@ namespace Barotrauma.Networking case ServerPacketHeader.STARTGAME: startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(inc), false); break; + case ServerPacketHeader.STARTGAMEFINALIZE: + ReadStartGameFinalize(inc); + break; case ServerPacketHeader.ENDGAME: string endMessage = inc.ReadString(); bool missionSuccessful = inc.ReadBoolean(); @@ -767,7 +774,26 @@ namespace Barotrauma.Networking break; } } - + + private void ReadStartGameFinalize(IReadMessage inc) + { + int levelEqualityCheckVal = inc.ReadInt32(); + + if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal) + { + string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed + + ", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" + + ", mirrored: " + Level.Loaded.Mirrored + ")."; + GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + throw new Exception(errorMsg); + } + + GameMain.GameSession.Mission?.ClientReadInitial(inc); + + roundInitialized = true; + } + + private void OnDisconnect() { if (SteamManager.IsInitialized) @@ -782,7 +808,11 @@ namespace Barotrauma.Networking string[] splitMsg = disconnectMsg.Split('/'); DisconnectReason disconnectReason = DisconnectReason.Unknown; - if (splitMsg.Length > 0) { Enum.TryParse(splitMsg[0], out disconnectReason); } + bool disconnectReasonIncluded = false; + if (splitMsg.Length > 0) + { + if (Enum.TryParse(splitMsg[0], out disconnectReason)) { disconnectReasonIncluded = true; } + } if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage) { @@ -857,7 +887,8 @@ namespace Barotrauma.Networking DebugConsole.NewMessage("Attempting to reconnect..."); - string msg = TextManager.GetServerMessage(disconnectMsg); + //if the first part of the message is the disconnect reason Enum, don't include it in the popup message + string msg = TextManager.GetServerMessage(disconnectReasonIncluded ? string.Join('/', splitMsg.Skip(1)) : disconnectMsg); msg = string.IsNullOrWhiteSpace(msg) ? TextManager.Get("ConnectionLostReconnecting") : msg + '\n' + TextManager.Get("ConnectionLostReconnecting"); @@ -871,6 +902,7 @@ namespace Barotrauma.Networking } else { + connected = false; connectCancelled = true; string msg = ""; @@ -1113,9 +1145,11 @@ namespace Barotrauma.Networking EndVoteTickBox.Selected = false; + roundInitialized = false; + int seed = inc.ReadInt32(); string levelSeed = inc.ReadString(); - int levelEqualityCheckVal = inc.ReadInt32(); + //int levelEqualityCheckVal = inc.ReadInt32(); float levelDifficulty = inc.ReadSingle(); byte losMode = inc.ReadByte(); @@ -1151,6 +1185,8 @@ namespace Barotrauma.Networking serverSettings.ReadMonsterEnabled(inc); + bool includesFinalize = inc.ReadBoolean(); inc.ReadPadBits(); + GameModePreset gameMode = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier); MultiPlayerCampaign campaign = GameMain.NetLobbyScreen.SelectedMode == GameMain.GameSession?.GameMode.Preset && gameMode == GameMain.NetLobbyScreen.SelectedMode ? @@ -1238,7 +1274,44 @@ namespace Barotrauma.Networking mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]); } - GameMain.GameSession.Mission?.ClientReadInitial(inc); + if (includesFinalize) + { + ReadStartGameFinalize(inc); + } + + //wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message + DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30); + while (DateTime.Now < timeOut) + { + if (!connected) + { + yield return CoroutineStatus.Success; + } + if (roundInitialized) + { + break; + } + try + { + clientPeer?.Update((float)Timing.Step); + } + catch (Exception e) + { + DebugConsole.ThrowError("There was an error initializing the round.", e, true); + roundInitialized = false; + break; + } + + //waiting for a STARTGAMEFINALIZE message + yield return CoroutineStatus.Running; + } + + if (!roundInitialized) + { + DebugConsole.ThrowError("Error while starting the round (did not receive STARTROUNDFINALIZE message from the server). Stopping the round..."); + CoroutineManager.StartCoroutine(EndGame("")); + yield return CoroutineStatus.Failure; + } if (GameMain.GameSession.Submarine.IsFileCorrupted) { @@ -1258,23 +1331,12 @@ namespace Barotrauma.Networking } } - if (Level.Loaded.EqualityCheckVal != levelEqualityCheckVal) - { - string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server (seed: " + Level.Loaded.Seed + - ", sub: " + Submarine.MainSub.Name + " (" + Submarine.MainSub.MD5Hash.ShortHash + ")" + - ", mirrored: " + Level.Loaded.Mirrored + ")."; - DebugConsole.ThrowError(errorMsg, createMessageBox: true); - GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + levelSeed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); - CoroutineManager.StartCoroutine(EndGame("")); - yield return CoroutineStatus.Failure; - } - if (respawnAllowed) { respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.UsingShuttle ? GameMain.NetLobbyScreen.SelectedShuttle : null); } GameMain.GameSession.EventManager.PreloadContent(contentToPreload); - ServerSettings.ServerDetailsChanged = true; gameStarted = true; + ServerSettings.ServerDetailsChanged = true; GameMain.GameScreen.Select(); @@ -1422,7 +1484,7 @@ namespace Barotrauma.Networking existingClient.Muted = tc.Muted; existingClient.AllowKicking = tc.AllowKicking; GameMain.NetLobbyScreen.SetPlayerNameAndJobPreference(existingClient); - if (tc.CharacterID > 0) + if (Screen.Selected != GameMain.NetLobbyScreen && tc.CharacterID > 0) { existingClient.Character = Entity.FindEntityByID(tc.CharacterID) as Character; if (existingClient.Character == null) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/LidgrenClientPeer.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/LidgrenClientPeer.cs index d315062c3..41f8f65af 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/LidgrenClientPeer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/LidgrenClientPeer.cs @@ -91,6 +91,7 @@ namespace Barotrauma.Networking return; } + incomingLidgrenMessages.Clear(); netClient.ReadMessages(incomingLidgrenMessages); foreach (NetIncomingMessage inc in incomingLidgrenMessages) @@ -107,8 +108,6 @@ namespace Barotrauma.Networking break; } } - - incomingLidgrenMessages.Clear(); } private void HandleDataMessage(NetIncomingMessage inc) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaClient/ClientSource/Physics/PhysicsBody.cs index ee5078feb..c362ef365 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Physics/PhysicsBody.cs @@ -49,11 +49,11 @@ namespace Barotrauma { if (!forceColor) { - if (!body.Enabled) + if (!FarseerBody.Enabled) { color = Color.Black; } - else if (!body.Awake) + else if (!FarseerBody.Awake) { color = Color.Blue; } @@ -71,7 +71,7 @@ namespace Barotrauma if (drawOffset != Vector2.Zero) { - Vector2 pos = ConvertUnits.ToDisplayUnits(body.Position); + Vector2 pos = ConvertUnits.ToDisplayUnits(FarseerBody.Position); if (Submarine != null) pos += Submarine.DrawPosition; GUI.DrawLine(spriteBatch, @@ -160,7 +160,7 @@ namespace Barotrauma Vector2 newPosition = SimPosition; float? newRotation = null; - bool awake = body.Awake; + bool awake = FarseerBody.Awake; Vector2 newVelocity = LinearVelocity; float? newAngularVelocity = null; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs index 63a40aa07..d140b5d4e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs @@ -228,7 +228,7 @@ namespace Barotrauma { var sub = child.UserData as Submarine; if (sub == null) { return; } - child.Visible = string.IsNullOrEmpty(filter) ? true : sub.Name.ToLower().Contains(filter.ToLower()); + child.Visible = string.IsNullOrEmpty(filter) ? true : sub.DisplayName.ToLower().Contains(filter.ToLower()); } } @@ -292,7 +292,7 @@ namespace Barotrauma { var textBlock = new GUITextBlock( new RectTransform(new Vector2(1, 0.1f), subList.Content.RectTransform) { MinSize = new Point(0, 30) }, - ToolBox.LimitString(sub.Name, GUI.Font, subList.Rect.Width - 65), style: "ListBoxElement") + ToolBox.LimitString(sub.DisplayName, GUI.Font, subList.Rect.Width - 65), style: "ListBoxElement") { ToolTip = sub.Description, UserData = sub diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index 7e39e5ea4..cf8ebbd08 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -319,6 +319,14 @@ namespace Barotrauma GameMain.Instance.OnResolutionChanged += () => { + foreach (GUIComponent c in Frame.GetAllChildren()) + { + if (c.Style != null) + { + c.ApplySizeRestrictions(c.Style); + } + } + if (innerFrame != null) { innerFrame.RectTransform.MaxSize = new Point(int.MaxValue, GameMain.GraphicsHeight - 50); @@ -565,7 +573,8 @@ namespace Barotrauma // Chat input - var chatRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), socialHolder.RectTransform), true) + var chatRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), socialHolder.RectTransform), + isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true }; @@ -652,9 +661,13 @@ namespace Barotrauma } }; clientHiddenElements.Add(StartButton); - bottomBar.RectTransform.MinSize = new Point(0, (int)Math.Max(ReadyToStartBox.RectTransform.MinSize.Y / 0.75f, StartButton.RectTransform.MinSize.Y)); + GameMain.Instance.OnResolutionChanged += () => + { + bottomBar.RectTransform.MinSize = + new Point(0, (int)Math.Max(ReadyToStartBox.RectTransform.MinSize.Y / 0.75f, StartButton.RectTransform.MinSize.Y)); + }; //autorestart ------------------------------------------------------------------ @@ -700,6 +713,10 @@ namespace Barotrauma clientHiddenElements.Add(SettingsButton); lobbyHeader.RectTransform.MinSize = new Point(0, Math.Max(ServerName.Rect.Height, SettingsButton.Rect.Height)); + GameMain.Instance.OnResolutionChanged += () => + { + lobbyHeader.RectTransform.MinSize = new Point(0, Math.Max(ServerName.Rect.Height, SettingsButton.Rect.Height)); + }; GUILayoutGroup lobbyContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), infoFrameContent.RectTransform), isHorizontal: true) { @@ -806,6 +823,10 @@ namespace Barotrauma }; shuttleList.ListBox.RectTransform.MinSize = new Point(250, 0); shuttleHolder.RectTransform.MinSize = new Point(0, shuttleList.RectTransform.Children.Max(c => c.MinSize.Y)); + GameMain.Instance.OnResolutionChanged += () => + { + shuttleHolder.RectTransform.MinSize = new Point(0, shuttleList.RectTransform.Children.Max(c => c.MinSize.Y)); + }; subPreviewContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), rightColumn.RectTransform), style: null); subPreviewContainer.RectTransform.SizeChanged += () => @@ -866,7 +887,7 @@ namespace Barotrauma }; levelDifficultyScrollBar = new GUIScrollBar(new RectTransform(new Vector2(0.25f, 1.0f), miscSettingsHolder.RectTransform), style: "GUISlider", barSize: 0.2f) { - Step = 0.05f, + Step = 0.01f, Range = new Vector2(0.0f, 100.0f), ToolTip = TextManager.Get("leveldifficultyexplanation"), OnReleased = (scrollbar, value) => @@ -883,7 +904,9 @@ namespace Barotrauma levelDifficultyScrollBar.OnMoved = (scrollbar, value) => { if (EventManagerSettings.List.Count == 0) { return true; } - difficultyName.Text = EventManagerSettings.List[Math.Min((int)Math.Floor(value * EventManagerSettings.List.Count), EventManagerSettings.List.Count - 1)].Name; + difficultyName.Text = + EventManagerSettings.List[Math.Min((int)Math.Floor(value * EventManagerSettings.List.Count), EventManagerSettings.List.Count - 1)].Name + + " (" + ((int)Math.Round(scrollbar.BarScrollValue)) + " %)"; difficultyName.TextColor = ToolBox.GradientLerp(scrollbar.BarScroll, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red); return true; }; @@ -944,6 +967,10 @@ namespace Barotrauma style: "GameModeIcon." + mode.Identifier, scaleToFit: true); modeFrame.RectTransform.MinSize = new Point(0, (int)(modeContent.Children.Sum(c => c.Rect.Height + modeContent.AbsoluteSpacing) / modeContent.RectTransform.RelativeSize.Y)); + GameMain.Instance.OnResolutionChanged += () => + { + modeFrame.RectTransform.MinSize = new Point(0, (int)(modeContent.Children.Sum(c => c.Rect.Height + modeContent.AbsoluteSpacing) / modeContent.RectTransform.RelativeSize.Y)); + }; } var gameModeSpecificFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1.0f), gameModeBackground.RectTransform), style: null); @@ -1286,6 +1313,8 @@ namespace Barotrauma if (GameMain.Client == null) return; spectateButton.Visible = true; spectateButton.Enabled = true; + + StartButton.Visible = false; } public void SetCampaignCharacterInfo(CharacterInfo newCampaignCharacterInfo) @@ -1482,17 +1511,17 @@ namespace Barotrauma private void CreateJobVariantTooltip(JobPrefab jobPrefab, int variant, GUIComponent parentSlot) { - jobVariantTooltip = new GUIFrame(new RectTransform(new Point((int)(500 * GUI.Scale), (int)(200 * GUI.Scale)), GUI.Canvas, pivot: Pivot.TopRight), + jobVariantTooltip = new GUIFrame(new RectTransform(new Point((int)(500 * GUI.Scale), (int)(200 * GUI.Scale)), GUI.Canvas, pivot: Pivot.BottomRight), style: "GUIToolTip") { UserData = new Pair(jobPrefab, variant) }; - jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.Right, parentSlot.Rect.Bottom); + jobVariantTooltip.RectTransform.AbsoluteOffset = new Point(parentSlot.Rect.Right, parentSlot.Rect.Y); var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), jobVariantTooltip.RectTransform, Anchor.Center)) { Stretch = true, - RelativeSpacing = 0.02f + AbsoluteSpacing = (int)(15 * GUI.Scale) }; string name = @@ -1506,10 +1535,18 @@ namespace Barotrauma ""; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), description, wrap: true, font: GUI.SmallFont); - new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform, Anchor.BottomLeft), - onDraw: (sb, component) => { DrawJobVariantItems(sb, component, new Pair(jobPrefab, variant)); }); - jobVariantTooltip.RectTransform.MinSize = new Point(0, content.RectTransform.Children.Sum(c => c.Rect.Height)); + var itemIdentifiers = jobPrefab.ItemIdentifiers[variant] + .Distinct() + .Where(id => jobPrefab.ShowItemPreview[variant][id]); + + int itemsPerRow = 5; + int rows = (int)Math.Max(Math.Ceiling(itemIdentifiers.Count() / (float)itemsPerRow), 1); + + new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.4f * rows), content.RectTransform, Anchor.BottomLeft), + onDraw: (sb, component) => { DrawJobVariantItems(sb, component, new Pair(jobPrefab, variant), itemsPerRow); }); + + jobVariantTooltip.RectTransform.MinSize = new Point(0, content.RectTransform.Children.Sum(c => c.Rect.Height + content.AbsoluteSpacing)); } public bool ToggleSpectate(GUITickBox tickBox) @@ -2302,7 +2339,7 @@ namespace Barotrauma } } - private void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, Pair jobPrefab) + private void DrawJobVariantItems(SpriteBatch spriteBatch, GUICustomComponent component, Pair jobPrefab, int itemsPerRow) { var itemIdentifiers = jobPrefab.First.ItemIdentifiers[jobPrefab.Second] .Distinct() @@ -2311,18 +2348,29 @@ namespace Barotrauma Point slotSize = new Point(component.Rect.Height); int spacing = (int)(5 * GUI.Scale); int slotCount = itemIdentifiers.Count(); + int slotCountPerRow = Math.Min(slotCount, itemsPerRow); + int rows = (int)Math.Max(Math.Ceiling(itemIdentifiers.Count() / (float)itemsPerRow), 1); - float totalWidth = slotSize.X * slotCount + spacing * (slotCount - 1); + float totalWidth = slotSize.X * slotCountPerRow + spacing * (slotCountPerRow - 1); + float totalHeight = slotSize.Y * rows + spacing * (rows - 1); if (totalWidth > component.Rect.Width) { - slotSize = slotSize.Multiply(component.Rect.Width / totalWidth); + slotSize = new Point( + Math.Min((int)Math.Floor((slotSize.X - spacing) * (component.Rect.Width / totalWidth)), + (int)Math.Floor((slotSize.Y - spacing) * (component.Rect.Height / totalHeight)))); } int i = 0; + Rectangle tooltipRect = Rectangle.Empty; + string tooltip = null; foreach (string itemIdentifier in itemIdentifiers) { if (!(MapEntityPrefab.Find(null, identifier: itemIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab)) { continue; } - Vector2 slotPos = new Vector2(component.Rect.X + (slotSize.X + spacing) * i, component.Rect.Center.Y - slotSize.Y / 2); + int row = (int)Math.Floor(i / (float)slotCountPerRow); + int slotsPerThisRow = Math.Min((slotCount - row * slotCountPerRow), slotCountPerRow); + Vector2 slotPos = new Vector2( + component.Rect.Center.X + (slotSize.X + spacing) * (i % slotCountPerRow - slotsPerThisRow * 0.5f), + component.Rect.Bottom - (rows * (slotSize.Y + spacing)) + (slotSize.Y + spacing) * row); Rectangle slotRect = new Rectangle(slotPos.ToPoint(), slotSize); Inventory.SlotSpriteSmall.Draw(spriteBatch, slotPos, @@ -2342,10 +2390,15 @@ namespace Barotrauma if (slotRect.Contains(PlayerInput.MousePosition)) { - GUIComponent.DrawToolTip(spriteBatch, itemPrefab.Name + '\n' + itemPrefab.Description, slotRect); + tooltipRect = slotRect; + tooltip = itemPrefab.Name + '\n' + itemPrefab.Description; } i++; } + if (!string.IsNullOrEmpty(tooltip)) + { + GUIComponent.DrawToolTip(spriteBatch, tooltip, tooltipRect); + } } public void NewChatMessage(ChatMessage message) @@ -2754,7 +2807,7 @@ namespace Barotrauma GUIFrame innerFrame = null; List outfitPreviews = jobPrefab.GetJobOutfitSprites(Gender.Male, useInventoryIcon: true, out var maxDimensions); - innerFrame = new GUIFrame(new RectTransform(Vector2.One * 0.8f, parent.RectTransform, Anchor.Center), style: null) + innerFrame = new GUIFrame(new RectTransform(Vector2.One * 0.85f, parent.RectTransform, Anchor.Center), style: null) { CanBeFocused = false }; @@ -2781,11 +2834,18 @@ namespace Barotrauma } } + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.35f), parent.RectTransform, Anchor.BottomCenter), style: "OuterGlow") + { + Color = Color.Black, + HoverColor = Color.Black, + SelectedColor = Color.Black + }; + var textBlock = new GUITextBlock( innerFrame.CountChildren == 0 ? new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center) : - new RectTransform(new Vector2(selectedByPlayer ? 0.65f : 0.95f, 0.3f), parent.RectTransform, Anchor.TopCenter), - jobPrefab.Name, wrap: true, textAlignment: Alignment.TopCenter) + new RectTransform(new Vector2(selectedByPlayer ? 0.65f : 0.95f, 0.3f), parent.RectTransform, Anchor.BottomCenter), + jobPrefab.Name, wrap: true, textAlignment: Alignment.BottomCenter) { Padding = Vector4.Zero, HoverColor = Color.Transparent, @@ -2794,6 +2854,7 @@ namespace Barotrauma CanBeFocused = false, AutoScaleHorizontal = true }; + textBlock.TextAlignment = textBlock.WrappedText.Contains('\n') ? Alignment.BottomCenter : Alignment.Center; textBlock.RectTransform.SizeChanged += () => { textBlock.TextScale = 1.0f; }; return retVal; @@ -3043,7 +3104,7 @@ namespace Barotrauma } // Info button - new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopLeft, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, + new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.BottomLeft, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.075f) }, style: "GUIButtonInfo") { UserData = jobPrefab, @@ -3051,7 +3112,7 @@ namespace Barotrauma }; // Remove button - new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.TopRight, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.05f) }, + new GUIButton(new RectTransform(new Vector2(0.15f), slot.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.BothWidth) { RelativeOffset = new Vector2(0.075f) }, style: "GUICancelButton") { UserData = i, @@ -3105,7 +3166,7 @@ namespace Barotrauma { float relativeSize = 0.2f; - var btn = new GUIButton(new RectTransform(new Vector2(relativeSize), slot.RectTransform, Anchor.BottomCenter, scaleBasis: ScaleBasis.BothHeight) + var btn = new GUIButton(new RectTransform(new Vector2(relativeSize), slot.RectTransform, Anchor.TopCenter, scaleBasis: ScaleBasis.BothHeight) { RelativeOffset = new Vector2(relativeSize * 1.05f * (variantIndex - (variantCount - 1) / 2.0f), 0.02f) }, (variantIndex + 1).ToString(), style: "JobVariantButton") { diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 45fd7ce62..182804f13 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index f04954404..6aba9f784 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index aadcbd667..eebf92ae5 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index a5455845e..8bf4f6e59 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 65d8e04c3..5f33868c6 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index fd3e99141..8a3ce2122 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -283,6 +283,15 @@ namespace Barotrauma GameMain.NetLobbyScreen.SetBotCount(botCount); NewMessage("Set the number of bots to " + botCount, Color.White); }); + AssignOnClientRequestExecute("botcount", (Client client, Vector2 cursorPos, string[] args) => + { + if (args.Length < 1 || GameMain.Server == null) return; + int botCount = GameMain.Server.ServerSettings.BotCount; + int.TryParse(args[0], out botCount); + GameMain.NetLobbyScreen.SetBotCount(botCount); + NewMessage(client.Name + " set the number of bots to " + botCount, Color.White); + GameMain.Server.SendConsoleMessage("Set the number of bots to " + botCount, client); + }); AssignOnExecute("botspawnmode", (string[] args) => { @@ -297,6 +306,20 @@ namespace Barotrauma NewMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", Color.White); } }); + AssignOnClientRequestExecute("botspawnmode", (Client client, Vector2 cursorPos, string[] args) => + { + if (args.Length < 1 || GameMain.Server == null) return; + if (Enum.TryParse(args[0], true, out BotSpawnMode spawnMode)) + { + GameMain.NetLobbyScreen.SetBotSpawnMode(spawnMode); + NewMessage(client.Name + " set bot spawn mode to " + spawnMode, Color.White); + GameMain.Server.SendConsoleMessage("Set bot spawn mode to " + spawnMode, client); + } + else + { + GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid bot spawn mode. (Valid modes are Fill and Normal)", client); + } + }); AssignOnExecute("killdisconnectedtimer", (string[] args) => { @@ -304,13 +327,29 @@ namespace Barotrauma float seconds = GameMain.Server.ServerSettings.KillDisconnectedTime; if (float.TryParse(args[0], out seconds)) { - NewMessage("Set kill disconnected timer to " + seconds + " seconds", Color.White); + seconds = Math.Max(0, seconds); + NewMessage("Set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds), Color.White); } else { NewMessage("\"" + args[0] + "\" is not a valid duration.", Color.White); } }); + AssignOnClientRequestExecute("killdisconnectedtimer", (Client client, Vector2 cursorPos, string[] args) => + { + if (args.Length < 1 || GameMain.Server == null) return; + float seconds = GameMain.Server.ServerSettings.KillDisconnectedTime; + if (float.TryParse(args[0], out seconds)) + { + seconds = Math.Max(0, seconds); + GameMain.Server.SendConsoleMessage("Set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds), client); + NewMessage(client.Name + " set kill disconnected timer to " + ToolBox.SecondsToReadableTime(seconds), Color.White); + } + else + { + GameMain.Server.SendConsoleMessage("\"" + args[0] + "\" is not a valid duration.", client); + } + }); AssignOnExecute("autorestart", (string[] args) => { @@ -329,10 +368,6 @@ namespace Barotrauma if (GameMain.Server.ServerSettings.AutoRestartInterval <= 0) GameMain.Server.ServerSettings.AutoRestartInterval = 10; GameMain.Server.ServerSettings.AutoRestartTimer = GameMain.Server.ServerSettings.AutoRestartInterval; GameMain.Server.ServerSettings.AutoRestart = enabled; -#if CLIENT - //TODO: reimplement - GameMain.NetLobbyScreen.SetAutoRestart(enabled, GameMain.Server.AutoRestartTimer); -#endif GameMain.NetLobbyScreen.LastUpdateID++; } NewMessage(GameMain.Server.ServerSettings.AutoRestart ? "Automatic restart enabled." : "Automatic restart disabled.", Color.White); @@ -357,10 +392,6 @@ namespace Barotrauma GameMain.Server.ServerSettings.AutoRestart = false; NewMessage("Autorestart disabled.", Color.White); } -#if CLIENT - //TODO: redo again - GameMain.NetLobbyScreen.SetAutoRestart(GameMain.Server.AutoRestart, GameMain.Server.AutoRestartTimer); -#endif GameMain.NetLobbyScreen.LastUpdateID++; } } @@ -677,6 +708,13 @@ namespace Barotrauma GameMain.Server.ServerSettings.KarmaEnabled = !GameMain.Server.ServerSettings.KarmaEnabled; NewMessage(GameMain.Server.ServerSettings.KarmaEnabled ? "Karma system enabled." : "Karma system disabled.", Color.LightGreen); }); + AssignOnClientRequestExecute("togglekarma", (Client client, Vector2 cursorWorldPos, string[] args) => + { + if (GameMain.Server == null) return; + GameMain.Server.ServerSettings.KarmaEnabled = !GameMain.Server.ServerSettings.KarmaEnabled; + NewMessage((GameMain.Server.ServerSettings.KarmaEnabled ? "Karma system enabled by " : "Karma system disabled by ") + client.Name, Color.LightGreen); + GameMain.Server.SendConsoleMessage(GameMain.Server.ServerSettings.KarmaEnabled ? "Karma system enabled." : "Karma system disabled.", client); + }); AssignOnExecute("resetkarma", (string[] args) => { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Signal/ConnectionPanel.cs index 8e67ea25d..e11921678 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Items/Components/Signal/ConnectionPanel.cs @@ -91,7 +91,10 @@ namespace Barotrauma.Items.Components } existingWire.RemoveConnection(item); - item.GetComponent()?.DisconnectedWires.Add(existingWire); + if (existingWire.Item.ParentInventory == null) + { + item.GetComponent()?.DisconnectedWires.Add(existingWire); + } if (!wires.Any(w => w.Contains(existingWire))) { @@ -103,8 +106,14 @@ namespace Barotrauma.Items.Components GameServer.Log(c.Character.LogName + " disconnected a wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Wiring); - if (!clientSideDisconnectedWires.Contains(existingWire)) + if (existingWire.Item.ParentInventory != null) { + //in an inventory and not connected to anything -> the wire cannot have any nodes + existingWire.ClearConnections(); + } + else if (!clientSideDisconnectedWires.Contains(existingWire)) + { + //not in an inventory, not connected to anything, not hanging loose from any panel -> must be dropped existingWire.Item.Drop(c.Character); } } @@ -145,7 +154,8 @@ namespace Barotrauma.Items.Components { if (disconnectedWire.Connections[0] == null && disconnectedWire.Connections[1] == null && - !clientSideDisconnectedWires.Contains(disconnectedWire)) + !clientSideDisconnectedWires.Contains(disconnectedWire) && + disconnectedWire.Item.ParentInventory == null) { disconnectedWire.Item.Drop(c.Character); GameServer.Log(c.Character.LogName + " dropped " + disconnectedWire.Name, ServerLog.MessageType.Inventory); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index 9f8f75f83..1e5098cae 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -627,7 +627,7 @@ namespace Barotrauma.Networking //game already started -> send start message immediately if (gameStarted) { - SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClient); + SendStartMessage(roundStartSeed, GameMain.GameSession.Level.Seed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClient, true); } } break; @@ -1796,6 +1796,8 @@ namespace Barotrauma.Networking campaign.Map.SelectRandomLocation(preferUndiscovered: true); } + SendStartMessage(roundStartSeed, campaign.Map.SelectedConnection.Level.Seed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients, false); + GameMain.GameSession.StartRound(campaign.Map.SelectedConnection.Level, reloadSub: true, loadSecondSub: teamCount > 1, @@ -1808,6 +1810,8 @@ namespace Barotrauma.Networking } else { + SendStartMessage(roundStartSeed, GameMain.NetLobbyScreen.LevelSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients, false); + GameMain.GameSession.StartRound(GameMain.NetLobbyScreen.LevelSeed, serverSettings.SelectedLevelDifficulty, teamCount > 1); Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage); Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage); @@ -1946,7 +1950,7 @@ namespace Barotrauma.Networking GameAnalyticsManager.AddDesignEvent("Traitors:" + (TraitorManager == null ? "Disabled" : "Enabled")); - SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients); + SendRoundStartFinalize(connectedClients); yield return CoroutineStatus.Running; @@ -1965,22 +1969,21 @@ namespace Barotrauma.Networking yield return CoroutineStatus.Success; } - private void SendStartMessage(int seed, Submarine selectedSub, GameModePreset selectedMode, List clients) + private void SendStartMessage(int seed, string levelSeed, Submarine selectedSub, GameModePreset selectedMode, List clients, bool includesFinalize) { foreach (Client client in clients) { - SendStartMessage(seed, selectedSub, selectedMode, client); + SendStartMessage(seed, levelSeed, selectedSub, selectedMode, client, includesFinalize); } } - private void SendStartMessage(int seed, Submarine selectedSub, GameModePreset selectedMode, Client client) + private void SendStartMessage(int seed, string levelSeed, Submarine selectedSub, GameModePreset selectedMode, Client client, bool includesFinalize) { IWriteMessage msg = new WriteOnlyMessage(); msg.Write((byte)ServerPacketHeader.STARTGAME); msg.Write(seed); - msg.Write(GameMain.GameSession.Level.Seed); - msg.Write(GameMain.GameSession.Level.EqualityCheckVal); + msg.Write(levelSeed); msg.Write(serverSettings.SelectedLevelDifficulty); msg.Write((byte)GameMain.Config.LosMode); @@ -2020,6 +2023,32 @@ namespace Barotrauma.Networking serverSettings.WriteMonsterEnabled(msg); + msg.Write(includesFinalize); msg.WritePadBits(); + if (includesFinalize) + { + msg.Write(GameMain.GameSession.Level.EqualityCheckVal); + GameMain.GameSession.Mission?.ServerWriteInitial(msg, client); + } + + //GameMain.GameSession.Mission?.ServerWriteInitial(msg, client); + + serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); + } + + private void SendRoundStartFinalize(List clients) + { + foreach (Client client in clients) + { + SendRoundStartFinalize(client); + } + } + + private void SendRoundStartFinalize(Client client) + { + IWriteMessage msg = new WriteOnlyMessage(); + msg.Write((byte)ServerPacketHeader.STARTGAMEFINALIZE); + + msg.Write(GameMain.GameSession.Level.EqualityCheckVal); GameMain.GameSession.Mission?.ServerWriteInitial(msg, client); serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs index 0eca5efc2..77b9c4c1e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs @@ -184,29 +184,25 @@ namespace Barotrauma.Networking { XDocument doc = new XDocument(new XElement("serversettings")); - SerializableProperty.SerializeProperties(this, doc.Root, true); - doc.Root.SetAttributeValue("name", ServerName); doc.Root.SetAttributeValue("public", IsPublic); doc.Root.SetAttributeValue("port", Port); #if USE_STEAM doc.Root.SetAttributeValue("queryport", QueryPort); #endif + doc.Root.SetAttributeValue("password", password ?? ""); + doc.Root.SetAttributeValue("enableupnp", EnableUPnP); - doc.Root.SetAttributeValue("autorestart", autoRestart); - if (!string.IsNullOrEmpty(password)) - { - doc.Root.SetAttributeValue("password", password); - } - doc.Root.SetAttributeValue("LevelDifficulty", ((int)selectedLevelDifficulty).ToString()); - + + doc.Root.SetAttributeValue("ServerMessage", ServerMessageText); + doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes)); doc.Root.SetAttributeValue("AllowedClientNameChars", string.Join(",", AllowedClientNameChars.Select(c => c.First + "-" + c.Second))); - - doc.Root.SetAttributeValue("ServerMessage", ServerMessageText); + + SerializableProperty.SerializeProperties(this, doc.Root, true); XmlWriterSettings settings = new XmlWriterSettings { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaServer/ServerSource/Physics/PhysicsBody.cs index 6874cab35..fc5d6e837 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Physics/PhysicsBody.cs @@ -15,10 +15,10 @@ namespace Barotrauma msg.Write(SimPosition.Y); #if DEBUG - if (Math.Abs(body.LinearVelocity.X) > MaxVel || - Math.Abs(body.LinearVelocity.Y) > MaxVel) + if (Math.Abs(FarseerBody.LinearVelocity.X) > MaxVel || + Math.Abs(FarseerBody.LinearVelocity.Y) > MaxVel) { - DebugConsole.ThrowError("Item velocity out of range (" + body.LinearVelocity + ")"); + DebugConsole.ThrowError("Item velocity out of range (" + FarseerBody.LinearVelocity + ")"); } #endif @@ -27,20 +27,20 @@ namespace Barotrauma if (!FarseerBody.FixedRotation) { - msg.WriteRangedSingle(MathUtils.WrapAngleTwoPi(body.Rotation), 0.0f, MathHelper.TwoPi, 8); + msg.WriteRangedSingle(MathUtils.WrapAngleTwoPi(FarseerBody.Rotation), 0.0f, MathHelper.TwoPi, 8); } if (FarseerBody.Awake) { - body.Enabled = true; - body.LinearVelocity = new Vector2( - MathHelper.Clamp(body.LinearVelocity.X, -MaxVel, MaxVel), - MathHelper.Clamp(body.LinearVelocity.Y, -MaxVel, MaxVel)); - msg.WriteRangedSingle(body.LinearVelocity.X, -MaxVel, MaxVel, 12); - msg.WriteRangedSingle(body.LinearVelocity.Y, -MaxVel, MaxVel, 12); + FarseerBody.Enabled = true; + FarseerBody.LinearVelocity = new Vector2( + MathHelper.Clamp(FarseerBody.LinearVelocity.X, -MaxVel, MaxVel), + MathHelper.Clamp(FarseerBody.LinearVelocity.Y, -MaxVel, MaxVel)); + msg.WriteRangedSingle(FarseerBody.LinearVelocity.X, -MaxVel, MaxVel, 12); + msg.WriteRangedSingle(FarseerBody.LinearVelocity.Y, -MaxVel, MaxVel, 12); if (!FarseerBody.FixedRotation) { - body.AngularVelocity = MathHelper.Clamp(body.AngularVelocity, -MaxAngularVel, MaxAngularVel); - msg.WriteRangedSingle(body.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8); + FarseerBody.AngularVelocity = MathHelper.Clamp(FarseerBody.AngularVelocity, -MaxAngularVel, MaxAngularVel); + msg.WriteRangedSingle(FarseerBody.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8); } } diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index b84dd777e..aa171331a 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.9.705.0 + 0.9.707.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index 97c5d3597..7a70b5557 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -28,6 +28,8 @@ + + diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Order.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Order.cs index aa52360d2..16ba0c9ea 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Order.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Order.cs @@ -20,13 +20,7 @@ namespace Barotrauma class Order { public static Dictionary Prefabs { get; private set; } - public static Dictionary OrderCategoryIcons { get; private set; } - public static Sprite StartNode { get; private set; } - public static Sprite ShortcutNode { get; private set; } - public static Sprite ExpandNode { get; private set; } - public static Sprite NodeContainer { get; private set; } - public static Sprite HotkeyContainer { get; private set; } - public static Sprite CommandBackground { get; private set; } + public static Dictionary> OrderCategoryIcons { get; private set; } public static List PrefabList { get; private set; } public static Order GetPrefab(string identifier) { @@ -52,7 +46,30 @@ namespace Barotrauma public readonly string Identifier; - public readonly Color Color; + private Color? color; + public Color Color + { + get + { + if (color.HasValue) + { + return color.Value; + } + else if (OrderCategoryIcons.TryGetValue(Category, out Tuple sprite)) + { + return sprite.Item2; + } + else + { + return Color.White; + } + } + private set + { + color = value; + } + } + //if true, the order is issued to all available characters public bool TargetAllCharacters; @@ -80,7 +97,7 @@ namespace Barotrauma static Order() { Prefabs = new Dictionary(); - OrderCategoryIcons = new Dictionary(); + OrderCategoryIcons = new Dictionary>(); foreach (ContentFile file in GameMain.Instance.GetFilesOfType(ContentType.Orders)) { @@ -125,7 +142,7 @@ namespace Barotrauma else if (name.Equals("ordercategory", StringComparison.OrdinalIgnoreCase)) { var category = (OrderCategory)Enum.Parse(typeof(OrderCategory), element.GetAttributeString("category", "undefined"), true); - if (OrderCategoryIcons.TryGetValue(category, out Sprite duplicate)) + if (OrderCategoryIcons.ContainsKey(category)) { if (allowOverriding || sourceElement.IsOverride()) { @@ -142,35 +159,8 @@ namespace Barotrauma if (spriteElement != null) { var sprite = new Sprite(spriteElement, lazyLoad: true); - OrderCategoryIcons.Add(category, sprite); - } - } - else - { - var spriteElement = element.GetChildElement("sprite"); - if (spriteElement != null) - { - switch (name.ToLowerInvariant()) - { - case "startnode": - StartNode = new Sprite(spriteElement, lazyLoad: true); - break; - case "shortcutnode": - ShortcutNode = new Sprite(spriteElement, lazyLoad: true); - break; - case "expandnode": - ExpandNode = new Sprite(spriteElement, lazyLoad: true); - break; - case "nodecontainer": - NodeContainer = new Sprite(spriteElement, lazyLoad: true); - break; - case "hotkeycontainer": - HotkeyContainer = new Sprite(spriteElement, lazyLoad: true); - break; - case "commandbackground": - CommandBackground = new Sprite(spriteElement, lazyLoad: true); - break; - } + var color = element.GetAttributeColor("color", Color.White); + OrderCategoryIcons.Add(category, new Tuple(sprite, color)); } } } @@ -201,7 +191,7 @@ namespace Barotrauma } ItemIdentifiers = orderElement.GetAttributeStringArray("targetitemidentifiers", new string[0], trim: true, convertToLowerInvariant: true); - Color = orderElement.GetAttributeColor("color", Color.White); + color = orderElement.GetAttributeColor("color"); FadeOutTime = orderElement.GetAttributeFloat("fadeouttime", 0.0f); UseController = orderElement.GetAttributeBool("usecontroller", false); TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false); @@ -271,6 +261,7 @@ namespace Barotrauma AppropriateJobs = prefab.AppropriateJobs; FadeOutTime = prefab.FadeOutTime; Weight = prefab.Weight; + Category = prefab.Category; OrderGiver = orderGiver; TargetEntity = targetEntity; @@ -300,22 +291,22 @@ namespace Barotrauma } for (int i = 0; i < AppropriateJobs.Length; i++) { - if (character.Info.Job.Prefab.Identifier.ToLowerInvariant() == AppropriateJobs[i].ToLowerInvariant()) return true; + if (character.Info.Job.Prefab.Identifier.ToLowerInvariant() == AppropriateJobs[i].ToLowerInvariant()) { return true; } } return false; } public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "") { - orderOption = orderOption ?? ""; + orderOption ??= ""; string messageTag = (givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf." : "OrderDialog.") + Identifier; - if (!string.IsNullOrEmpty(orderOption)) messageTag += "." + orderOption; + if (!string.IsNullOrEmpty(orderOption)) { messageTag += "." + orderOption; } - if (targetCharacterName == null) targetCharacterName = ""; - if (targetRoomName == null) targetRoomName = ""; + if (targetCharacterName == null) { targetCharacterName = ""; } + if (targetRoomName == null) { targetRoomName = ""; } string msg = TextManager.GetWithVariables(messageTag, new string[2] { "[name]", "[roomname]" }, new string[2] { targetCharacterName, targetRoomName }, new bool[2] { false, true }, true); - if (msg == null) return ""; + if (msg == null) { return ""; } return msg; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs index 69a8abfe6..6490858b9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs @@ -78,8 +78,12 @@ namespace Barotrauma if (frozen == value) return; frozen = value; - - Collider.PhysEnabled = !frozen; + + Collider.FarseerBody.LinearDamping = frozen ? (1.5f / (float)Timing.Step) : 0.0f; + Collider.FarseerBody.AngularDamping = frozen ? (1.5f / (float)Timing.Step) : 0.0f; + Collider.FarseerBody.IgnoreGravity = frozen; + + //Collider.PhysEnabled = !frozen; if (frozen && MainLimb != null) MainLimb.PullJointWorldAnchorB = MainLimb.SimPosition; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index 05f292559..111cb6230 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -312,15 +312,7 @@ namespace Barotrauma selectedCharacter.selectedBy = null; selectedCharacter = value; if (selectedCharacter != null) - { selectedCharacter.selectedBy = this; -#if CLIENT - if (Inventory != null) - { - Inventory.ToggleInventory(true); - } -#endif - } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index 2cebaafd6..c70c2556f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -1269,7 +1269,7 @@ namespace Barotrauma { //if the command is not defined client-side, we'll relay it anyway because it may be a custom command at the server's side GameMain.Client.SendConsoleCommand(command); - NewMessage("Server command: " + command, Color.White); + NewMessage("Server command: " + command, Color.Cyan); return; } else if (GameMain.Client.HasConsoleCommandPermission(splitCommand[0].ToLowerInvariant())) @@ -1277,7 +1277,7 @@ namespace Barotrauma if (matchingCommand.RelayToServer) { GameMain.Client.SendConsoleCommand(command); - NewMessage("Server command: " + command, Color.White); + NewMessage("Server command: " + command, Color.Cyan); } else { @@ -1424,8 +1424,6 @@ namespace Barotrauma var variant = job != null ? Rand.Range(0, job.Variants, Rand.RandSync.Server) : 0; CharacterInfo characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: job, variant: variant); spawnedCharacter = Character.Create(characterInfo, spawnPosition, ToolBox.RandomSeed(8)); - spawnedCharacter.GiveJobItems(spawnPoint); - if (GameMain.GameSession != null) { if (GameMain.GameSession.GameMode != null && !GameMain.GameSession.GameMode.IsSinglePlayer) @@ -1437,6 +1435,7 @@ namespace Barotrauma GameMain.GameSession.CrewManager.AddCharacter(spawnedCharacter); #endif } + spawnedCharacter.GiveJobItems(spawnPoint); } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs index f28580947..22173bdad 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs @@ -56,7 +56,7 @@ namespace Barotrauma get { return activeEvents; } } - public EventManager(GameSession session) + public EventManager() { isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; } @@ -70,21 +70,9 @@ namespace Barotrauma pendingEventSets.Clear(); selectedEvents.Clear(); - var suitableSettings = EventManagerSettings.List.FindAll(s => - level.Difficulty >= s.MinLevelDifficulty && - level.Difficulty <= s.MaxLevelDifficulty); - - if (suitableSettings.Count == 0) - { - DebugConsole.ThrowError("No suitable event manager settings found for the selected level (difficulty " + level.Difficulty + ")"); - settings = EventManagerSettings.List[Rand.Int(EventManagerSettings.List.Count, Rand.RandSync.Server)]; - } - else - { - settings = suitableSettings[Rand.Int(suitableSettings.Count, Rand.RandSync.Server)]; - } - this.level = level; + SelectSettings(); + var initialEventSet = SelectRandomEvents(ScriptedEventSet.List); if (initialEventSet != null) { @@ -102,6 +90,32 @@ namespace Barotrauma eventCoolDown = 0.0f; } + private void SelectSettings() + { + if (EventManagerSettings.List.Count == 0) + { + throw new InvalidOperationException("Could not select EventManager settings (no settings loaded)."); + } + if (level == null) + { + throw new InvalidOperationException("Could not select EventManager settings (level not set)."); + } + + var suitableSettings = EventManagerSettings.List.FindAll(s => + level.Difficulty >= s.MinLevelDifficulty && + level.Difficulty <= s.MaxLevelDifficulty); + + if (suitableSettings.Count == 0) + { + DebugConsole.ThrowError("No suitable event manager settings found for the selected level (difficulty " + level.Difficulty + ")"); + settings = EventManagerSettings.List[Rand.Int(EventManagerSettings.List.Count, Rand.RandSync.Server)]; + } + else + { + settings = suitableSettings[Rand.Int(suitableSettings.Count, Rand.RandSync.Server)]; + } + } + public IEnumerable GetFilesToPreload() { foreach (List eventList in selectedEvents.Values) @@ -310,6 +324,18 @@ namespace Barotrauma roundDuration += deltaTime; + if (settings == null) + { + DebugConsole.ThrowError("Event settings not set before updating EventManager. Attempting to select..."); + SelectSettings(); + if (settings == null) + { + DebugConsole.ThrowError("Could not select EventManager settings. Disabling EventManager for the round..."); + Enabled = false; + return; + } + } + eventThreshold += settings.EventThresholdIncrease * deltaTime; if (eventCoolDown > 0.0f) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs b/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs index b3b294452..a769afa54 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs @@ -17,9 +17,18 @@ namespace Barotrauma.Extensions /// /// Randomizes the list in place. /// - public static void RandomizeList(this List source) + public static void RandomizeList(this List list) { - source.Sort((x, y) => Rand.Value().CompareTo(Rand.Value())); + //Fisher-Yates shuffle + int n = list.Count; + while (n > 1) + { + n--; + int k = Rand.Int(n + 1); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } } public static T GetRandom(this IEnumerable source, Func predicate, Rand.RandSync randSync = Rand.RandSync.Unsynced) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs index 1a1393d35..b77825408 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/AutoItemPlacer.cs @@ -82,7 +82,8 @@ namespace Barotrauma // Another pass for items with containers because also they can spawn inside other items (like smg magazine) prefabsWithContainer.ForEach(i => SpawnItems(i)); // Spawn items that don't have containers last - prefabsWithoutContainer.Randomize().ForEach(i => SpawnItems(i)); + prefabsWithoutContainer.RandomizeList(); + prefabsWithoutContainer.ForEach(i => SpawnItems(i)); if (OutputDebugInfo) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index 563793bda..3964464a1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -91,7 +91,7 @@ namespace Barotrauma Submarine.MainSub = submarine; this.Submarine = submarine; GameMain.GameSession = this; - EventManager = new EventManager(this); + EventManager = new EventManager(); this.SavePath = savePath; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs index 1f2b97c96..c6cc4d6d8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs @@ -16,7 +16,6 @@ namespace Barotrauma partial class CharacterInventory : Inventory { - private const int hotkeyCount = 5; private Character character; public InvSlotType[] SlotTypes @@ -53,7 +52,16 @@ namespace Barotrauma { DebugConsole.ThrowError("Error in the inventory config of \"" + character.SpeciesName + "\" - " + slotTypeNames[i] + " is not a valid inventory slot type."); } - SlotTypes[i] = parsedSlotType; + SlotTypes[i] = parsedSlotType; + switch (SlotTypes[i]) + { + //case InvSlotType.Head: + //case InvSlotType.OuterClothes: + case InvSlotType.LeftHand: + case InvSlotType.RightHand: + hideEmptySlot[i] = true; + break; + } } InitProjSpecific(element); @@ -122,25 +130,25 @@ namespace Barotrauma /// /// If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equipped into its respective limbslot /// - public bool TryPutItemWithAutoEquipCheck(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false) + public bool TryPutItemWithAutoEquipCheck(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true) { // Does not auto-equip the item if specified and no suitable any slot found (for example handcuffs are not auto-equipped) if (item.AllowedSlots.Contains(InvSlotType.Any)) { var wearable = item.GetComponent(); - if (wearable != null && !wearable.AutoEquipWhenFull && CheckIfAnySlotAvailable(item, false, preferNonHotkeys) == -1) + if (wearable != null && !wearable.AutoEquipWhenFull && CheckIfAnySlotAvailable(item, false) == -1) { return false; } } - return TryPutItem(item, user, allowedSlots, createNetworkEvent, preferNonHotkeys); + return TryPutItem(item, user, allowedSlots, createNetworkEvent); } /// /// If there is room, puts the item in the inventory and returns true, otherwise returns false /// - public override bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false) + public override bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true) { if (allowedSlots == null || !allowedSlots.Any()) return false; @@ -164,7 +172,7 @@ namespace Barotrauma //try to place the item in a LimbSlot.Any slot if that's allowed if (allowedSlots.Contains(InvSlotType.Any) && item.AllowedSlots.Contains(InvSlotType.Any)) { - int freeIndex = CheckIfAnySlotAvailable(item, inWrongSlot, preferNonHotkeys); + int freeIndex = CheckIfAnySlotAvailable(item, inWrongSlot); if (freeIndex > -1) { PutItem(item, freeIndex, user, true, createNetworkEvent); @@ -207,7 +215,12 @@ namespace Barotrauma #if CLIENT if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; } #endif - bool removeFromOtherSlots = item.ParentInventory != this || (placedInSlot == -1 && inWrongSlot); + bool removeFromOtherSlots = item.ParentInventory != this; + if (placedInSlot == -1 && inWrongSlot) + { + if (!hideEmptySlot[i] || SlotTypes[currentSlot] != InvSlotType.Any) removeFromOtherSlots = true; + } + PutItem(item, i, user, removeFromOtherSlots, createNetworkEvent); item.Equip(character); placedInSlot = i; @@ -219,19 +232,16 @@ namespace Barotrauma return placedInSlot > -1; } - public int CheckIfAnySlotAvailable(Item item, bool inWrongSlot, bool preferNonHotkeys) + public int CheckIfAnySlotAvailable(Item item, bool inWrongSlot) { - for (int i = 0; i < capacity; i++) - { - if (SlotTypes[i] != InvSlotType.Any) continue; - if (Items[i] == item) + for (int i = 0; i < capacity; i++) { - return i; + if (SlotTypes[i] != InvSlotType.Any) continue; + if (Items[i] == item) + { + return i; + } } - } - - if (!preferNonHotkeys) - { for (int i = 0; i < capacity; i++) { if (SlotTypes[i] != InvSlotType.Any) continue; @@ -246,45 +256,11 @@ namespace Barotrauma return i; } - } - else - { - int hotkeysCounted = 0; - // First go through non-hotkeyed slots - for (int i = 0; i < capacity; i++) - { - if (SlotTypes[i] != InvSlotType.Any) continue; - hotkeysCounted++; - - if (hotkeysCounted <= hotkeyCount) continue; - - if (inWrongSlot) - { - if (Items[i] != item && Items[i] != null) continue; - } - else - { - if (Items[i] != null) continue; - } - -#if CLIENT - if (!inventoryOpen) - { - ToggleInventory(); - } -#endif - - return i; - } - - // Then redo with no preference - return CheckIfAnySlotAvailable(item, inWrongSlot, false); - } return -1; } - public override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool avoidHotkeys = false) + public override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) { if (index < 0 || index >= Items.Length) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs index bfd1cc524..1c7ab61cf 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs @@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components public virtual bool OnPicked(Character picker) { - if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots, true, true)) + if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots)) { if (!picker.HasSelectedItem(item) && item.body != null) item.body.Enabled = false; this.picker = picker; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WifiComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WifiComponent.cs index 15b450a98..a1da8e1d1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WifiComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WifiComponent.cs @@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components private string prevSignal; - [Serialize(Character.TeamType.None, false, description: "WiFi components can only communicate with components that have the same Team ID.")] + [Serialize(Character.TeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.")] public Character.TeamType TeamID { get; set; } [Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.")] diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs index 7ad737a90..94663b33e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Wire.cs @@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components } } - if (newNodeIndex == 0) + if (newNodeIndex == 0 && nodes.Count > 1) { nodes.Insert(0, nodePos); } @@ -499,7 +499,7 @@ namespace Barotrauma.Items.Components } } - private void ClearConnections(Character user = null) + public void ClearConnections(Character user = null) { nodes.Clear(); sections.Clear(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs index 17353c018..2a46248ef 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs @@ -14,6 +14,7 @@ namespace Barotrauma protected readonly int capacity; public Item[] Items; + protected bool[] hideEmptySlot; public bool Locked; @@ -31,6 +32,7 @@ namespace Barotrauma this.Owner = owner; Items = new Item[capacity]; + hideEmptySlot = new bool[capacity]; #if CLIENT this.slotsPerRow = slotsPerRow; @@ -39,8 +41,15 @@ namespace Barotrauma { DraggableIndicator = GUI.Style.GetComponentStyle("GUIDragIndicator").Sprites[GUIComponent.ComponentState.None][0].Sprite; - EquipIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(137, 10, 112, 25), new Vector2(0.5f, 1f), 0); - EquipIndicator.size = new Vector2(EquipIndicator.SourceRect.Width * 0.682f, EquipIndicator.SourceRect.Height * 0.682f); + slotHotkeySprite = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(258, 7, 120, 120), null, 0); + + EquippedIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 137, 87, 16), new Vector2(0.5f, 0.5f), 0); + EquippedHoverIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 157, 87, 16), new Vector2(0.5f, 0.5f), 0); + EquippedClickedIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 177, 87, 16), new Vector2(0.5f, 0.5f), 0); + + UnequippedIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 197, 87, 16), new Vector2(0.5f, 0.5f), 0); + UnequippedHoverIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 217, 87, 16), new Vector2(0.5f, 0.5f), 0); + UnequippedClickedIndicator = new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(550, 237, 87, 16), new Vector2(0.5f, 0.5f), 0); } #endif } @@ -102,7 +111,7 @@ namespace Barotrauma /// /// If there is room, puts the item in the inventory and returns true, otherwise returns false /// - public virtual bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true, bool avoidHotkeys = false) + public virtual bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true) { int slot = FindAllowedSlot(item); if (slot < 0) return false; @@ -111,7 +120,7 @@ namespace Barotrauma return true; } - public virtual bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool avoidHotkeys = false) + public virtual bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) { if (i < 0 || i >= Items.Length) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index 557f0bdbb..d516afc30 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -1986,7 +1986,14 @@ namespace Barotrauma body.ResetDynamics(); if (dropper != null) { - body.SetTransform(dropper.SimPosition, 0.0f); + if (body.Removed) + { + DebugConsole.ThrowError("Failed to drop the item \"" + Name + "\" (body has been removed)."); + } + else + { + body.SetTransform(dropper.SimPosition, 0.0f); + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemInventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemInventory.cs index 831d85aa3..793062786 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemInventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemInventory.cs @@ -47,9 +47,9 @@ namespace Barotrauma return (item != null && Items[i] == null && container.CanBeContained(item)); } - public override bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true, bool preferNonHotkeys = false) + public override bool TryPutItem(Item item, Character user, List allowedSlots = null, bool createNetworkEvent = true) { - bool wasPut = base.TryPutItem(item, user, allowedSlots, createNetworkEvent, preferNonHotkeys); + bool wasPut = base.TryPutItem(item, user, allowedSlots, createNetworkEvent); if (wasPut) { @@ -68,9 +68,9 @@ namespace Barotrauma return wasPut; } - public override bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true, bool preferNonHotkeys = false) + public override bool TryPutItem(Item item, int i, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true) { - bool wasPut = base.TryPutItem(item, i, allowSwapping, allowCombine, user, createNetworkEvent, preferNonHotkeys); + bool wasPut = base.TryPutItem(item, i, allowSwapping, allowCombine, user, createNetworkEvent); if (wasPut) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs index ed54d4672..cd4ea4dc3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/NetworkMember.cs @@ -60,6 +60,7 @@ namespace Barotrauma.Networking QUERY_STARTGAME, //ask the clients whether they're ready to start STARTGAME, //start a new round + STARTGAMEFINALIZE, //finalize round initialization ENDGAME, TRAITOR_MESSAGE, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs index 8d4242e75..15de71604 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Physics/PhysicsBody.cs @@ -88,14 +88,13 @@ namespace Barotrauma Circle, Rectangle, Capsule, HorizontalCapsule }; - private static List list = new List(); + private static readonly List list = new List(); public static List List { get { return list; } } - //the farseer physics body of the item - private Body body; + protected Vector2 prevPosition; protected float prevRotation; @@ -105,6 +104,12 @@ namespace Barotrauma private Vector2 drawPosition; private float drawRotation; + public bool Removed + { + get; + private set; + } + public Vector2 LastSentPosition { get; @@ -114,7 +119,7 @@ namespace Barotrauma private Shape bodyShape; public float height, width, radius; - private float density; + private readonly float density; //the direction the item is facing (for example, a gun has to be //flipped horizontally if the Character holding it turns around) @@ -198,7 +203,7 @@ namespace Barotrauma isEnabled = value; try { - if (isEnabled) body.Enabled = isPhysEnabled; else body.Enabled = false; + if (isEnabled) FarseerBody.Enabled = isPhysEnabled; else FarseerBody.Enabled = false; } catch (Exception e) { @@ -206,7 +211,7 @@ namespace Barotrauma if (UserData != null) DebugConsole.NewMessage("PhysicsBody UserData: " + UserData.GetType().ToString(), Color.Red); if (GameMain.World.ContactManager == null) DebugConsole.NewMessage("ContactManager is null!", Color.Red); else if (GameMain.World.ContactManager.BroadPhase == null) DebugConsole.NewMessage("Broadphase is null!", Color.Red); - if (body.FixtureList == null) DebugConsole.NewMessage("FixtureList is null!", Color.Red); + if (FarseerBody.FixtureList == null) DebugConsole.NewMessage("FixtureList is null!", Color.Red); if (UserData is Entity entity) { @@ -218,18 +223,18 @@ namespace Barotrauma public bool PhysEnabled { - get { return body.Enabled; } - set { isPhysEnabled = value; if (Enabled) body.Enabled = value; } + get { return FarseerBody.Enabled; } + set { isPhysEnabled = value; if (Enabled) FarseerBody.Enabled = value; } } public Vector2 SimPosition { - get { return body.Position; } + get { return FarseerBody.Position; } } public Vector2 Position { - get { return ConvertUnits.ToDisplayUnits(body.Position); } + get { return ConvertUnits.ToDisplayUnits(FarseerBody.Position); } } public Vector2 PrevPosition @@ -239,7 +244,7 @@ namespace Barotrauma public float Rotation { - get { return body.Rotation; } + get { return FarseerBody.Rotation; } } /// @@ -249,27 +254,27 @@ namespace Barotrauma public Vector2 LinearVelocity { - get { return body.LinearVelocity; } + get { return FarseerBody.LinearVelocity; } set { if (!IsValidValue(value, "velocity", -1000.0f, 1000.0f)) return; - body.LinearVelocity = value; + FarseerBody.LinearVelocity = value; } } public float AngularVelocity { - get { return body.AngularVelocity; } + get { return FarseerBody.AngularVelocity; } set { if (!IsValidValue(value, "angular velocity", -1000f, 1000f)) return; - body.AngularVelocity = value; + FarseerBody.AngularVelocity = value; } } public float Mass { - get { return body.Mass; } + get { return FarseerBody.Mass; } } public float Density @@ -277,36 +282,33 @@ namespace Barotrauma get { return density; } } - public Body FarseerBody - { - get { return body; } - } + public Body FarseerBody { get; private set; } public object UserData { - get { return body.UserData; } - set { body.UserData = value; } + get { return FarseerBody.UserData; } + set { FarseerBody.UserData = value; } } public float Friction { - set { body.Friction = value; } + set { FarseerBody.Friction = value; } } public BodyType BodyType { - get { return body.BodyType; } - set { body.BodyType = value; } + get { return FarseerBody.BodyType; } + set { FarseerBody.BodyType = value; } } public Category CollisionCategories { - set { body.CollisionCategories = value; } + set { FarseerBody.CollisionCategories = value; } } public Category CollidesWith { - set { body.CollidesWith = value; } + set { FarseerBody.CollidesWith = value; } } public PhysicsBody(XElement element, float scale = 1.0f) : this(element, Vector2.Zero, scale) { } @@ -316,15 +318,15 @@ namespace Barotrauma public PhysicsBody(float width, float height, float radius, float density) { CreateBody(width, height, radius, density); - LastSentPosition = body.Position; + LastSentPosition = FarseerBody.Position; list.Add(this); } public PhysicsBody(Body farseerBody) { - body = farseerBody; - if (body.UserData == null) body.UserData = this; - LastSentPosition = body.Position; + FarseerBody = farseerBody; + if (FarseerBody.UserData == null) FarseerBody.UserData = this; + LastSentPosition = FarseerBody.Position; list.Add(this); } @@ -335,13 +337,13 @@ namespace Barotrauma float width = ConvertUnits.ToSimUnits(colliderParams.Width) * colliderParams.Ragdoll.LimbScale; density = 10; CreateBody(width, height, radius, density); - body.BodyType = BodyType.Dynamic; - body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; - body.CollisionCategories = Physics.CollisionCharacter; - body.AngularDamping = 5.0f; - body.FixedRotation = true; - body.Friction = 0.05f; - body.Restitution = 0.05f; + FarseerBody.BodyType = BodyType.Dynamic; + FarseerBody.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; + FarseerBody.CollisionCategories = Physics.CollisionCharacter; + FarseerBody.AngularDamping = 5.0f; + FarseerBody.FixedRotation = true; + FarseerBody.Friction = 0.05f; + FarseerBody.Restitution = 0.05f; SetTransformIgnoreContacts(position, 0.0f); LastSentPosition = position; list.Add(this); @@ -354,13 +356,13 @@ namespace Barotrauma float width = ConvertUnits.ToSimUnits(limbParams.Width) * limbParams.Ragdoll.LimbScale; density = limbParams.Density; CreateBody(width, height, radius, density); - body.BodyType = BodyType.Dynamic; - body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; - body.CollisionCategories = Physics.CollisionItem; - body.Friction = limbParams.Friction; - body.Restitution = limbParams.Restitution; - body.AngularDamping = limbParams.AngularDamping; - body.UserData = this; + FarseerBody.BodyType = BodyType.Dynamic; + FarseerBody.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel; + FarseerBody.CollisionCategories = Physics.CollisionItem; + FarseerBody.Friction = limbParams.Friction; + FarseerBody.Restitution = limbParams.Restitution; + FarseerBody.AngularDamping = limbParams.AngularDamping; + FarseerBody.UserData = this; SetTransformIgnoreContacts(position, 0.0f); LastSentPosition = position; list.Add(this); @@ -374,12 +376,12 @@ namespace Barotrauma density = element.GetAttributeFloat("density", 10.0f); CreateBody(width, height, radius, density); //Enum.TryParse(element.GetAttributeString("bodytype", "Dynamic"), out BodyType bodyType); - body.BodyType = BodyType.Dynamic; - body.CollisionCategories = Physics.CollisionItem; - body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform; - body.Friction = element.GetAttributeFloat("friction", 0.3f); - body.Restitution = element.GetAttributeFloat("restitution", 0.05f); - body.UserData = this; + FarseerBody.BodyType = BodyType.Dynamic; + FarseerBody.CollisionCategories = Physics.CollisionItem; + FarseerBody.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform; + FarseerBody.Friction = element.GetAttributeFloat("friction", 0.3f); + FarseerBody.Restitution = element.GetAttributeFloat("restitution", 0.05f); + FarseerBody.UserData = this; SetTransformIgnoreContacts(position, 0.0f); LastSentPosition = position; list.Add(this); @@ -393,16 +395,16 @@ namespace Barotrauma switch (bodyShape) { case Shape.Capsule: - body = GameMain.World.CreateCapsule(height, radius, density); + FarseerBody = GameMain.World.CreateCapsule(height, radius, density); break; case Shape.HorizontalCapsule: - body = GameMain.World.CreateCapsuleHorizontal(width, radius, density); + FarseerBody = GameMain.World.CreateCapsuleHorizontal(width, radius, density); break; case Shape.Circle: - body = GameMain.World.CreateCircle(radius, density); + FarseerBody = GameMain.World.CreateCircle(radius, density); break; case Shape.Rectangle: - body = GameMain.World.CreateRectangle(width, height, density); + FarseerBody = GameMain.World.CreateRectangle(width, height, density); break; default: throw new NotImplementedException(bodyShape.ToString()); @@ -567,15 +569,15 @@ namespace Barotrauma public void ResetDynamics() { - body.ResetDynamics(); + FarseerBody.ResetDynamics(); } public void ApplyLinearImpulse(Vector2 impulse) { - if (!IsValidValue(impulse / body.Mass, "new velocity", -1000f, 1000f)) return; + if (!IsValidValue(impulse / FarseerBody.Mass, "new velocity", -1000f, 1000f)) return; if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return; - body.ApplyLinearImpulse(impulse); + FarseerBody.ApplyLinearImpulse(impulse); } /// @@ -587,24 +589,24 @@ namespace Barotrauma if (!IsValidValue(maxVelocity, "max velocity")) return; Vector2 velocityAddition = impulse / Mass; - Vector2 newVelocity = body.LinearVelocity + velocityAddition; + Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition; float newSpeedSqr = newVelocity.LengthSquared(); if (newSpeedSqr > maxVelocity * maxVelocity) { newVelocity = newVelocity.ClampLength(maxVelocity); } - if (!IsValidValue((newVelocity - body.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; + if (!IsValidValue((newVelocity - FarseerBody.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; - body.ApplyLinearImpulse((newVelocity - body.LinearVelocity) * Mass); + FarseerBody.ApplyLinearImpulse((newVelocity - FarseerBody.LinearVelocity) * Mass); } public void ApplyLinearImpulse(Vector2 impulse, Vector2 point) { if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return; if (!IsValidValue(point, "point")) return; - if (!IsValidValue(impulse / body.Mass, "new velocity", -1000.0f, 1000.0f)) return; - body.ApplyLinearImpulse(impulse, point); + if (!IsValidValue(impulse / FarseerBody.Mass, "new velocity", -1000.0f, 1000.0f)) return; + FarseerBody.ApplyLinearImpulse(impulse, point); } /// @@ -617,18 +619,18 @@ namespace Barotrauma if (!IsValidValue(maxVelocity, "max velocity")) return; Vector2 velocityAddition = impulse / Mass; - Vector2 newVelocity = body.LinearVelocity + velocityAddition; + Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition; float newSpeedSqr = newVelocity.LengthSquared(); if (newSpeedSqr > maxVelocity * maxVelocity) { newVelocity = newVelocity.ClampLength(maxVelocity); } - if (!IsValidValue((newVelocity - body.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; + if (!IsValidValue((newVelocity - FarseerBody.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; - body.ApplyLinearImpulse((newVelocity - body.LinearVelocity) * Mass, point); - body.AngularVelocity = MathHelper.Clamp( - body.AngularVelocity, + FarseerBody.ApplyLinearImpulse((newVelocity - FarseerBody.LinearVelocity) * Mass, point); + FarseerBody.AngularVelocity = MathHelper.Clamp( + FarseerBody.AngularVelocity, -NetConfig.MaxPhysicsBodyAngularVelocity, NetConfig.MaxPhysicsBodyAngularVelocity); } @@ -636,7 +638,7 @@ namespace Barotrauma public void ApplyForce(Vector2 force) { if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; - body.ApplyForce(force); + FarseerBody.ApplyForce(force); } /// @@ -647,10 +649,10 @@ namespace Barotrauma if (!IsValidValue(maxVelocity, "max velocity")) return; Vector2 velocityAddition = force / Mass * (float)Timing.Step; - Vector2 newVelocity = body.LinearVelocity + velocityAddition; + Vector2 newVelocity = FarseerBody.LinearVelocity + velocityAddition; float newSpeedSqr = newVelocity.LengthSquared(); - if (newSpeedSqr > maxVelocity * maxVelocity && Vector2.Dot(body.LinearVelocity, force) > 0.0f) + if (newSpeedSqr > maxVelocity * maxVelocity && Vector2.Dot(FarseerBody.LinearVelocity, force) > 0.0f) { float newSpeed = (float)Math.Sqrt(newSpeedSqr); float maxVelAddition = maxVelocity - newSpeed; @@ -659,20 +661,20 @@ namespace Barotrauma } if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) return; - body.ApplyForce(force); + FarseerBody.ApplyForce(force); } public void ApplyForce(Vector2 force, Vector2 point) { if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; if (!IsValidValue(point, "point")) return; - body.ApplyForce(force, point); + FarseerBody.ApplyForce(force, point); } public void ApplyTorque(float torque) { if (!IsValidValue(torque, "torque")) return; - body.ApplyTorque(torque); + FarseerBody.ApplyTorque(torque); } public bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform = true) @@ -684,7 +686,7 @@ namespace Barotrauma if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false; if (!IsValidValue(rotation, "rotation")) return false; - body.SetTransform(simPosition, rotation); + FarseerBody.SetTransform(simPosition, rotation); if (setPrevTransform) { SetPrevTransform(simPosition, rotation); } return true; } @@ -698,7 +700,7 @@ namespace Barotrauma if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return false; if (!IsValidValue(rotation, "rotation")) return false; - body.SetTransformIgnoreContacts(ref simPosition, rotation); + FarseerBody.SetTransformIgnoreContacts(ref simPosition, rotation); if (setPrevTransform) { SetPrevTransform(simPosition, rotation); } return true; } @@ -718,9 +720,9 @@ namespace Barotrauma if (lerp) { - if (Vector2.DistanceSquared((Vector2)targetPosition, body.Position) < 10.0f * 10.0f) + if (Vector2.DistanceSquared((Vector2)targetPosition, FarseerBody.Position) < 10.0f * 10.0f) { - drawOffset = -((Vector2)targetPosition - (body.Position + drawOffset)); + drawOffset = -((Vector2)targetPosition - (FarseerBody.Position + drawOffset)); prevPosition = (Vector2)targetPosition; } else @@ -729,26 +731,26 @@ namespace Barotrauma } if (targetRotation.HasValue) { - rotationOffset = -MathUtils.GetShortestAngle(body.Rotation + rotationOffset, targetRotation.Value); + rotationOffset = -MathUtils.GetShortestAngle(FarseerBody.Rotation + rotationOffset, targetRotation.Value); } } - SetTransformIgnoreContacts((Vector2)targetPosition, targetRotation == null ? body.Rotation : (float)targetRotation); + SetTransformIgnoreContacts((Vector2)targetPosition, targetRotation == null ? FarseerBody.Rotation : (float)targetRotation); targetPosition = null; targetRotation = null; } public void MoveToPos(Vector2 simPosition, float force, Vector2? pullPos = null) { - if (pullPos == null) pullPos = body.Position; + if (pullPos == null) pullPos = FarseerBody.Position; if (!IsValidValue(simPosition, "position", -1e10f, 1e10f)) return; if (!IsValidValue(force, "force")) return; - Vector2 vel = body.LinearVelocity; + Vector2 vel = FarseerBody.LinearVelocity; Vector2 deltaPos = simPosition - (Vector2)pullPos; deltaPos *= force; - body.ApplyLinearImpulse((deltaPos - vel * 0.5f) * body.Mass, (Vector2)pullPos); + FarseerBody.ApplyLinearImpulse((deltaPos - vel * 0.5f) * FarseerBody.Mass, (Vector2)pullPos); } /// @@ -774,7 +776,7 @@ namespace Barotrauma } ApplyForce(dragForce + buoyancy, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); - ApplyTorque(body.AngularVelocity * body.Mass * -0.08f); + ApplyTorque(FarseerBody.AngularVelocity * FarseerBody.Mass * -0.08f); } public void Update() @@ -789,9 +791,9 @@ namespace Barotrauma public void UpdateDrawPosition() { - drawPosition = Timing.Interpolate(prevPosition, body.Position); + drawPosition = Timing.Interpolate(prevPosition, FarseerBody.Position); drawPosition = ConvertUnits.ToDisplayUnits(drawPosition + drawOffset); - drawRotation = Timing.InterpolateRotation(prevRotation, body.Rotation) + rotationOffset; + drawRotation = Timing.InterpolateRotation(prevRotation, FarseerBody.Rotation) + rotationOffset; } public void CorrectPosition(List positionBuffer, @@ -827,27 +829,29 @@ namespace Barotrauma /// Should the angles be wrapped. Set to false if it makes a difference whether the angle of the body is 0.0f or 360.0f. public void SmoothRotate(float targetRotation, float force = 10.0f, bool wrapAngle = true) { - float nextAngle = body.Rotation + body.AngularVelocity * (float)Timing.Step; + float nextAngle = FarseerBody.Rotation + FarseerBody.AngularVelocity * (float)Timing.Step; float angle = wrapAngle ? MathUtils.GetShortestAngle(nextAngle, targetRotation) : MathHelper.Clamp(targetRotation - nextAngle, -MathHelper.Pi, MathHelper.Pi); float torque = angle * 60.0f * (force / 100.0f); - if (body.BodyType == BodyType.Kinematic) + if (FarseerBody.BodyType == BodyType.Kinematic) { if (!IsValidValue(torque, "torque")) return; - body.AngularVelocity = torque; + FarseerBody.AngularVelocity = torque; } else { - ApplyTorque(body.Mass * torque); + ApplyTorque(FarseerBody.Mass * torque); } } public void Remove() { list.Remove(this); - GameMain.World.Remove(body); + GameMain.World.Remove(FarseerBody); + + Removed = true; DisposeProjSpecific(); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs index 87934f63b..8daa9feef 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs @@ -399,6 +399,12 @@ namespace Barotrauma return ParseColor(element.Attribute(name).Value); } + public static Color? GetAttributeColor(this XElement element, string name) + { + if (element == null || element.Attribute(name) == null) { return null; } + return ParseColor(element.Attribute(name).Value); + } + public static Color[] GetAttributeColorArray(this XElement element, string name, Color[] defaultValue) { if (element?.Attribute(name) == null) return defaultValue; diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index f8b005556..0209d7448 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub index 3486d0f92..2b8a98c78 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub and b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub index f18887fe6..9aa5c5807 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 43a6057ec..e723e58f2 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,169 +1,45 @@ --------------------------------------------------------------------------------------------------------- -v0.9.705.0 (Unstable) +v0.9.707.0 --------------------------------------------------------------------------------------------------------- -- Fixed server owner occasionally timing out if loading the round takes too long. -- Fixed server owner's character occasionally being killed due to round start timeouts. -- Reduced the severity of the burns caused by deusizine. -- Fixed report buttons doing nothing. -- Fixed report buttons being visible when not controlling a character. -- Fixed sonar markers being visible when not receiving a signal from a remotely controlled shuttle. -- Fixed wire being drawn twice in the connection panel interface if it's first connected from both ends and then dragged away from the connection. -- Fixed client-side giveperm/giverank. -- Added "killdisconnectedtime" command that can be used to set the time after a disconnected player's character gets automatically killed. -- Increased default killdisconnectedtime to 2 minutes. -- Fixed items that can be put in either hand slot, but not in the "Any" slots (e.g. duffel bag), being put into both hand slots. -- Fixed crashing when attempting to draw an inventory when not controlling a character (happened in the sub editor when selecting an item container in the character mode and switching to default mode). -- Fixed crashing if there's no audio device available. -- Fixed wire being drawn twice in the connection panel interface if it's first connected from both ends and then dragged away from the connection. -- Fixed inventory toggle button working unreliably. -- Fixed alarm buzzer not returning to the original rotation when the alarm stops. -- Fixed grenades sometimes not exploding client-side in multiplayer. -- Fixed characters letting go of ladders when opening the health interface. -- An attempt to fix occasional crashing when autoupdating workshop items. -- Improved crewlist scaling on small resolutions. -- Fixes to text overflows on small resolutions. +- Whoops, fixed crashing in multiplayer due to CrewManager attempting to access the single player chatbox. --------------------------------------------------------------------------------------------------------- -v0.9.704.0 (Unstable) +v0.9.706.0 --------------------------------------------------------------------------------------------------------- -- Numerous UI fixes again. -- Inventory remake. -- Fixed yet another bug that caused inventory items to get mixed up in the campaign. -- Fixed a bug that caused clients to get disconnected with an "invalid object header" error when a character has a large amount of different afflictions. -- Fixed monster missions occasionally causing "entity not found" errors in the multiplayer campaign. -- Rebalanced medical items: stat-boosting effects last much longer. -- Modified vigor buffs from steroids and hyperzine to be intantaneous instead of delayed. -- Nerfed opiate spawning. -- Updated localizations (new texts are now translated to all languages). -- Reduced the number of monsters inside ruins in high-difficulty levels, improving performance in those levels. -- New loading screen. -- Health interface improvements. -- Improvements to the command interface. -- Fixed clients failing to select a submarine file downloaded from the server if it replaced an older version of the sub. -- Fixed performance issues when two items with editable properties are in the inventory (for example a headset and a diving suit). -- Fixed header texts not being visible in Russian. -- Skill gain values are now moddable (see Content/SkillSettings.xml). -- Balanced skill levels and skill gains. -- Job variant descriptions and items are now shown when hovering the cursor over the job variant button. -- Fix bots occasionally getting stuck on water waypoints. -- Fixed bots occasionally letting go of ladders too soon when going to operate an item, causing them to fall down. -- The filter in the sub editor only searches from the currently selected category. -- Made Unstable watermark less intrusive in sub editor. -- Fixed chatbox disappearing when any character is accessing another character's inventory. -- Chatbox is hidden after the message has been sent if it was hidden when starting to enter the message. -- Fixed wifi components in the respawn shuttle being able to communicate with the main sub in non-combat missions. -- Fixed engine sound range being up to 20 times larger than it should be. -- Prevent dragging items while dragging inventories. -- Fixed monsters occasionally being able to attack characters inside the sub. -- Screwdrivers can be used as shivs! -- Fixed last wire node appearing to be outside the submarine if a character gets electrocuted while connecting the first end of the wire. -- Fixes to bugs that occasionally caused the client hosting a server to get silently disconnected from the server, getting stuck in a "limbo" where they can't interact with anything. -- Fix crash with command UI when there is no reactor on the sub. -- Fixed all items being highlighted in the multiplayer campaign store menu when another player buys something. -- Fixed setting the number of items to buy by typing a number in the text box being practically impossible in the multiplayer campaign store. -- Fixed grenades exploding multiple times if you throw one, pick it up before it explodes and rethrow it. -- Fixed equipped wire being shown twice in the wiring interface if you connect and disconnect it when the other end of the wire is not connected to anything. -- Fixed antibiotic glue being marked as a suitable treatment for gunshot wounds and internal damage, even though it only treats burns and bleeding. -- Fixed a waypoint issue in Orca's engineering compartment that prevented the bots from reaching the oxygen generator. - ---------------------------------------------------------------------------------------------------------- -v0.9.703.0 (Unstable) ---------------------------------------------------------------------------------------------------------- - -- Numerous UI fixes (layout improvements, improved scaling on different resolutions, fixed item interface overlaps, text overflow fixes). -- Added Korean translation. -- Numerous fixes to random events to better pace new level dimensions. -- Fixed non-host clients crashing when disconnecting from the server. -- Made giveperm/giverank commands suck less by allowing names, endpoints and SteamIDs instead of clientlist id, and allowing users to skip question prompt by adding rank or perm as a second parameter. -- Fixes to render order oddities (structures with a depth > 0.5 always rendering behind all items, inconsistent render order between sub editor and in-game). Now structures with a depth of >= 0.9 are always behind everything (and visible through the LOS effect), and item's sprite depth is capped to 0.9. -- Fixed engines causing crashes if MinVoltage is set to 0. -- Fixed multiplayer campaign saves appearing in the single player "load game" menu if they're placed in the singleplayer save folder (leading to a crash if a player starts to load the save). -- Fixed fabricator cancellation failing to be communicated under certain circumstances. -- Fixed fabricator and deconstructor operating faster when run on overvoltage, making it possible to fabricate/deconstruct things almost instantaneously by using relays. -- Fixed campaign store filter doing nothing. -- Potentially a fixed race condition when autoupdating Workshop items during loading screen. -- Fixed subinventories not opening when trying to heal an unconscious character. -- Fixed crashing when moving the nodes of a wire that's not connected to anything in the sub editor. -- Fixed inability to drag wires out of the wiring interface (even if they're disconnected from both ends). -- Another fix to inventory items occasionally getting messed up in multiplayer. -- Removed oxygenite shards as a product of deconstructing liquid oxygenite to prevent production loops. -- Fixed sonar showing everything around the sub when sending out a directional ping and switching to passive. -- Fixed inability to drag characters from room to another in alien ruins. -- Fixed excessively large tonic liquid collider. -- Fixed reactor's on/off state not being saved. -- Fixed crashing if a modded UI style contains multiple child styles with the same name. -- Wifi components in the respawn shuttle can't communicate with components in other submarines/shuttles. -- Fixed wire nodes getting misplaced when flipping wires in the sub editor. -- Fixed groups of items that include wires sometimes not getting placed at the position of the cursor when pasting them in the sub editor. - ---------------------------------------------------------------------------------------------------------- -v0.9.702.0 (Unstable) ---------------------------------------------------------------------------------------------------------- - -- UI overhaul -- Increased level sizes. -- Rebalanced monster spawns. -- Added a few new artifact missions. -- Fixed server not sending condition updates for inactive items, potentially causing the condition to get desynced when all of the components of the item go inactive. -- Fixed clients being unable to rejoin a SteamP2P server after they've left. -- Fix to inventory items occasionally getting mixed up in the campaign. -- Power consumption of damaged devices doesn't increase as much anymore. -- Player cap can be adjusted in the server settings. -- Fixed "cannot remove joints when the world is locked" error message when a character latched onto the sub is attacked. -- Fixed currents heavily slowing down the submarine regardless of the force or direction of the current. -- Improved name tag hiding. -- Fixed explosives that disappear after exploding (e.g. nuclear explosives) not playing the explosion sound. -- Fixed server list not listing passworded SteamP2P servers correctly. -- Reduced Daedalic splash screen volume further! -- Made tonic liquid purchasable. -- Subscribed Workshop items aren't shown in the Popular tab. -- Fixed some held items vibrating/twitching when moving. -- Fixed turrets emitting muzzle flash particles in an incorrect direction (the rotation of the particle was correct but the direction it flied towards not, which isn't noticeable with the non-moving vanilla particles). -- Fixed clients not relaying console commands that don't exist client-side to the server (i.e. custom commands implemented by a server mod can now be used by clients). -- Added combat priorities to alien weapons to allow bots to use them. - ---------------------------------------------------------------------------------------------------------- -v0.9.701.0 (Unstable) ---------------------------------------------------------------------------------------------------------- - -- Fixed SteamP2P server hosting. -- Fixed a bug that caused "missing entity" errors when joining mid-round. Occurred if entities were created or removed while the client was in the process of getting in sync with the server, for example if the other players were firing turrets while the client was joining. However, there may still be other bugs left that can cause the same "missing entity" error message. -- Cap the framerate to 200 FPS when VSync is off to prevent overloading the GPU. The cap can be adjusted by changing the "framelimit" attribute in config_player.xml. -- Rebalanced random events. -- Numerous fixes and improvements to Berilia. -- Give job items to humans spawned with the "spawn" command. -- Fixed content package hash mismatches between Windows and Linux. -- Fixed client crashing when disconnecting from the server as a host. -- Fixed clients crashing when an order message is received while in the server lobby. -- New sprites (that actually match the worn sprite) for dropped outfits. -- Slightly increased the view range of the coilguns and railguns. -- Cursor changes according to what it's hovered on (hand icon when on a button, caret icon when on a textbox, etc). -- The automatically placed items are not listed in the debug console by default anymore. Listing them can be re-enabled with the command "outfitdebug". -- Fixed crashing when saving structures into item assemblies. -- Fixed sub editor allowing vanilla subs to be deleted. - ---------------------------------------------------------------------------------------------------------- -v0.9.700.0 (Unstable) ---------------------------------------------------------------------------------------------------------- - -- Additional logging to diagnose event/entity ID errors: server and clients write a log file with a bunch of debug information the error happens. The files can be found in the ServerLogs folder after the error has occurred. If you get log files, please send them to us to help us diagnose these bugs! - -- Fixed "skip tutorials" returning to the main menu instead of opening the correct tab when starting a new game. -- Fixed crashing when an enemy takes damage from something else than a character. -- Fixed clients disconnecting if a sub with no version number (or a sub the client hasn't received yet) is selected. -- Fixed crash when spawning a human without specified job through the console. -- Fixed several bugs that were causing crashes due to race conditions in the server list. -- Made "showseed" console command usable by clients. -- Fixed accordion collider. -- Fixed doors not blocking hitscan weapons. -- Continue playing the main menu and editor musics from previous position instead of restarting when switching between screens. -- Added missing sonar display background to sonar monitor. -- Fuel rods now always return their steel on deconstruction. -- Constructing fuel rods now requires lead. -- Added an improved version of Typhon. -- Reworked Berilia. +- Reverted back to the old inventory UI (with updated graphics) for now. +- Miscellaneous UI layout fixes and improvements. +- Visual improvements to the health interface. +- Fixed bots spawned using console commands not getting assigned the correct team id on their headsets, causing them to be disabled in the command interface. +- Fixed team ids of character's headsets resetting after the first campaign round, preventing them from communicating via radio with the players/bots who are playing their first round. +- Fixed collider tunneling when client is slow to send inputs. Caused characters to occasionally noclip through walls when the connection or framerate is poor. +- Improved the way clients initialize rounds to prevent timeouts if loading the round takes too long. +- Fixed enemy team being visible in the command UI in combat missions. +- Adjusted repair times to better suit current skill balance. +- Medical item balancing. +- Command button is hidden when it cannot be used. +- Added a button to give orders to crew members directly. +- Don't close item UI when opening crew list with keybind. +- Allow toggling crew list with keybind in spectator and freecam modes. +- Fixed command UI hotkey navigation triggering inventory hotkey in the last step. +- Fixed command UI being immediately re-enabled after giving an order in clickless mode. +- Fixed sonar beacon UI overlapping with the chatbox. +- Fixed nav terminal's docking button overlapping with the chatbox. +- Marked "editsubs" a cheat command. +- Fixed cursor getting stuck to the dragging or hand state if dragging/highlighting a wire is interrupted by any other reason than letting go of the left mouse button (electrocution, getting killed, ending the round...) +- Improved server lobby scaling when switching resolutions. +- Fixed some server-side console commands not sending anything back to the client executing them, making it seem as if the command did nothing (botcount, botspawnmode, killdisconnectedtimer, togglekarma). +- Fixed progress bars having a bunch of dead space at the beginning and end (causing battery/repair sliders to appear empty when below ~5%, and full when above ~95%). +- Fixed CustomInterface crashing the game when selected if it contains no buttons/tickboxes. +- Fixed a couple of unfair ruin traps (rooms with coil/sensor placement that makes it impossible to pass through without getting zapped). +- Fixed tutorial video scaling when opened more than once. +- Lock portrait area, health bar, affliction icons and report buttons when using a turret. +- Fixed lower docking port ladder on Typhon 2. +- Safeguards to prevent EventManager from crashing the game due to missing EventManagerSettings (#2066). +- Some safeguards and extra error logging for case where the world field of a physics body seems to be null when dropping an item (#2252). +- Fixed a rare crash with the message "Compare() method returns inconsistent results" when autoplacing items in the sub. +- Fixed mechanic tutorial getting softlocked if the player never has an oxygen tank (or aluminium) and sodium in their inventory at the same time. I.e. if they deconstruct the oxygen tanks first and put the aluminium in the fabricator, and then get the sodium and put it in the fabricator. --------------------------------------------------------------------------------------------------------- v0.9.7.0 @@ -171,6 +47,8 @@ v0.9.7.0 UI/UX improvements: - Graphical and functional overhaul of all user interfaces. +- Cursor changes according to what it's hovered on (hand icon when on a button, caret icon when on a textbox, etc). +- New loading screen. - Better feedback on shooting. - Double clicking on an item moves it to the equipped inventory (e.g. ammo to the equipped weapon). - Periscopes can be deselected by pressing esc. @@ -190,6 +68,7 @@ UI/UX improvements: - Made docking indicators more visible on the sonar. - Show mic input level also when Push-to-Talk mode is selected to make it easier to adjust the level without having to switch the mode to Voice Activity. - Pressing the up arrow brings up previously sent chat messages, making it easier to resend them. +- The filter in the sub editor only searches from the currently selected category. Optimizations: - Major physics optimization. Most importantly, the physics are now multithreaded, making the game perform much better when there are large numbers of characters active. @@ -201,8 +80,16 @@ Optimizations: - Improved the performance of human AI (bots). Additions and changes: +- Added Korean translation. +- Added an improved version of Typhon. +- Reworked Berilia. - New shuttle, "Hemulen". - Merged Selkie and Bunyip into a new, improved shuttle (just called Selkie now). +- Increased level sizes. +- Rebalanced monster spawns. +- Balanced skill levels and skill gains. +- Skill gain values are now moddable (see Content/SkillSettings.xml). +- Added a few new artifact missions. - The job gear variants are not just visually different versions of the same item, but completely separate items. The job variants now allow the players to choose what kind of gear they want to spawn with, not just the look of the uniform. - Attachable items and wire nodes can now be freely placed around the character instead of always being placed at the position of the character. When attaching items/wires, there's a placement grid that makes it much easier to neatly attach/wire things mid-round. - The submarines now get automatically outfitted with a semi-random selection of supplies when starting a campaign. The items that have been manually placed in the submarine editor are kept as-is. @@ -219,8 +106,14 @@ Additions and changes: - Added subtle chromatic aberration & blur when suffering from heavy bloodloss. - Nerfed hull damage from plasma cutter and welding tool explosions. - Removed repair thresholds from items; any device that's not in perfect condition can now be repaired. +- Rebalanced medical items: stat-boosting effects last much longer. +- Modified vigor buffs from steroids and hyperzine to be intantaneous instead of delayed. - Tweaked opiates. They now do less overdose damage, produce a lower addiction for skilled users, but produce a serious addiction for unskilled users. - Bandages now have 3 uses. +- Removed oxygenite shards as a product of deconstructing liquid oxygenite to prevent production loops. +- Screwdrivers can be used as shivs! +- Fuel rods now always return their steel on deconstruction. +- Constructing fuel rods now requires lead. - Mouse button names are now translated. - Improved tiger thresher texture and lights. - Replaced the legacy ladder, stair and docking port sprites. @@ -250,6 +143,16 @@ Additions and changes: - Lowers number of dependencies, simplifying the installation process. - Introduces features that will translate into further stability and performance improvements in the future. - Switched from MP4 to WebM to minimize bloat and dependencies on patented tech. +- Cap the framerate to 200 FPS when VSync is off to prevent overloading the GPU. The cap can be adjusted by changing the "framelimit" attribute in config_player.xml. +- Wifi components in the respawn shuttle can't communicate with components in other submarines/shuttles. +- Power consumption of damaged devices doesn't increase as much anymore. +- Made tonic liquid purchasable. +- Added combat priorities to alien weapons to allow bots to use them. +- Improved name tag hiding. +- Give job items to humans spawned with the "spawn" command. +- Slightly increased the view range of the coilguns and railguns. +- Continue playing the main menu and editor music from the previous position instead of restarting when switching between screens. +- Marked "editsubs" a cheat command (so you can't just switch to the sub editor, spawn whatever you need and go back to the game without enabling cheats). AI: - Bots can now undock the sub. @@ -277,7 +180,7 @@ Monsters: - Revised Matriarch's behavior: Matriarchs now spawn in swarms, protected by other Hammerheads, and they try to keep the distance to the submarine. Boosted the explosion and increased the Spawn count. - Added new missions about Hammerhead Matriarch. - Revised all the creatures using the new behaviors. -- Changed how "attack when provoked" works. The previous state is now reset when the target changes. Also fixed several relate issues. Rename the property (requires action if custom characters use it). +- Changed how "attack when provoked" works. The previous state is now reset when the target changes. Also fixed several relate issues. Rename the property (requires action if custom characters use it). When a creature is attacked and "attack when provoked" is false and the attacker is not found in the predefined ai targets, the creature flees by default instead of just ignoring the attacker. - Refactored the eating behavior: fixes small creatures not being able to eat significantly larger creatures. - Adjusted the commonness and the reward of the Thresher swarm mission. - Improved the indoors escaping behavior. @@ -297,20 +200,34 @@ Monsters: - Fixed Mudraptors sometimes squeezing themselves towards doors without being able to attack them. - Fixed monsters not reacting to being fired with turrets unless they can target the attacker. - Fixed minor slipping in Mudraptor's walking animation. +- Weapons and tools now have ai targets that are only activated when the items are used -> shooting monsters should make you much more attractive target than just swimming peacefully around. Multiplayer: +- Fixed a bunch of bugs that caused "missing entity" errors. However, there are many different reasons the error can occur, so even though we have not run into the issue anymore during out testing rounds, there is still a chance it may occur in some situations. +- Fixed inventory items occasionally getting mixed up in the multiplayer campaign. +- Fixed a bug that caused clients to get disconnected with an "invalid object header" error when a character has a large amount of different afflictions. +- Fixed server owner occasionally timing out if loading the round takes too long. +- Fixed server owner's character occasionally being killed due to round start timeouts. - Fixed players not getting notified in any way when their connection to the server has timed out, allowing them to keep playing without being able to interact with anything. +- Made giveperm/giverank commands suck less by allowing names, endpoints and SteamIDs instead of clientlist id, and allowing users to skip question prompt by adding rank or perm as a second parameter. +- Fixed multiplayer campaign saves appearing in the single player "load game" menu if they're placed in the singleplayer save folder (leading to a crash if a player starts to load the save). +- Fixed server not sending condition updates for inactive items, potentially causing the condition to get desynced when all of the components of the item go inactive. - Fixed clients only being informed of the reason for their ban the moment they're banned, but not if they try to rejoin. - Fixed clients not attempting to reconnect to the server automatically when the connection is lost, forcing the client to rejoin the server manually. +- Fixed clients sometimes being able to noclip through walls when the framerate or connection is poor. - Karma system can be enabled/disabled in the "host server" menu. - Option to set the number of password retries before a ban. - Fixed voice chat indicators not working in the in-game crew list. - Moved "End Round" button to the pause menu. - Added a separate server log category for wiring. +- Fixed clients not relaying console commands that don't exist client-side to the server (i.e. custom commands implemented by a server mod can now be used by clients). +- Added "killdisconnectedtime" command that can be used to set the time after a disconnected player's character gets automatically killed. +- Increased default killdisconnectedtime to 2 minutes. +- Player cap can be adjusted in the server settings. +- Made "showseed" console command usable by clients. Bugfixes: -- Fixed camera position "twitching" when moving the cursor around while unconscious. -- Fixed texts in mission/traitor notifications occasionally overflowing outside the border of the message box. +- Fixes to render order oddities (structures with a depth > 0.5 always rendering behind all items, inconsistent render order between sub editor and in-game). Now structures with a depth of >= 0.9 are always behind everything (and visible through the LOS effect), and item's sprite depth is capped to 0.9. - Fixed background structures that are resizeable on both axes always being drawn behind other background structures regardless of the sprite depth. - Fixed Kastrull flooding when the drone undocks. - Fixed ballast pumps deteriorating in Kastrull's drone despite being unreachable by the players. @@ -319,6 +236,13 @@ Bugfixes: - Fixed charactes being unable to get through multi-layer walls from inside the sub (for example the walls above Humpback's command room). - Fixed plasma cutter not cutting through holes in walls. - Fixed melee weapons not working inside ruins due to the colliders that block subs from entering the ruins. +- Fixed fabricator cancellation failing to be communicated under certain circumstances. +- Fixed fabricator and deconstructor operating faster when run on overvoltage, making it possible to fabricate/deconstruct things almost instantaneously by using relays. +- Fixed camera position "twitching" when moving the cursor around while unconscious. +- Fixed crashing if there's no audio device available. +- Fixed texts in mission/traitor notifications occasionally overflowing outside the border of the message box. +- Fixed subinventories not opening when trying to heal an unconscious character. +- Fixed engines causing crashes if MinVoltage is set to 0. - Fixed small "twitch" when a character enters or exits a submarine. - Fixed starting a combat mission with just 1 player counting as winning the mission. - Fixed linked shuttles occasionally spawning at the wrong side of a sub's docking port. @@ -334,6 +258,28 @@ Bugfixes: - Fixed crashes in the character editor when creating a humanoid with incomplete limb definitions. - Fixed the character editor crashing if no textures were found. - Don't allow to save invalid texture paths in the character editor. +- Decreased the range of some of the motion sensors inside alien ruins to prevent certain rooms from being nearly impossible to get past without getting zapped by an alien coil. +- Fixed excessively large tonic liquid collider. +- Fixed accordion collider. +- Fixed doors not blocking hitscan weapons. +- Fixed sonar showing everything around the sub when sending out a directional ping and immediately switching to passive. +- Fixed inability to drag characters from room to another in alien ruins. +- Fixed crashing if a modded UI style contains multiple child styles with the same name. +- Fixed wire nodes getting misplaced when flipping wires in the sub editor. +- Fixed groups of items that include wires sometimes not getting placed at the position of the cursor when pasting them in the sub editor. +- Fixed currents heavily slowing down the submarine regardless of the force or direction of the current. +- Fixed some held items vibrating/twitching when moving. +- Fixed turrets emitting muzzle flash particles in an incorrect direction (the rotation of the particle was correct but the direction it flew towards not, which isn't noticeable with the non-moving vanilla particles). +- Fixed "skip tutorials" returning to the main menu instead of opening the correct tab when starting a new game. +- Fixed all items being highlighted in the multiplayer campaign store menu when another player buys something. +- Fixed setting the number of items to buy by typing a number in the text box being practically impossible in the multiplayer campaign store. +- Fixed grenades exploding multiple times if you throw one, pick it up before it explodes and rethrow it. +- Fixed bots occasionally letting go of ladders too soon when going to operate an item, causing them to fall down. +- Fixed some waypoint issues in Orca. +- Fixed wifi components in the respawn shuttle being able to communicate with the main sub in non-combat missions. +- Fixed engine sound range being up to 20 times larger than it should be. +- Fixed monsters occasionally being able to attack through walls. +- Fixed alarm buzzer not returning to the original rotation when the alarm stops. --------------------------------------------------------------------------------------------------------- v0.9.6.0 diff --git a/Barotrauma/BarotraumaShared/serversettings.xml b/Barotrauma/BarotraumaShared/serversettings.xml index e0d931d97..4960cd465 100644 --- a/Barotrauma/BarotraumaShared/serversettings.xml +++ b/Barotrauma/BarotraumaShared/serversettings.xml @@ -1,5 +1,18 @@  \ No newline at end of file + /> \ No newline at end of file diff --git a/Libraries/Farseer Physics Engine 3.5/Dynamics/Body.cs b/Libraries/Farseer Physics Engine 3.5/Dynamics/Body.cs index a5faa3177..7393a2401 100644 --- a/Libraries/Farseer Physics Engine 3.5/Dynamics/Body.cs +++ b/Libraries/Farseer Physics Engine 3.5/Dynamics/Body.cs @@ -723,6 +723,8 @@ namespace FarseerPhysics.Dynamics public void SetTransformIgnoreContacts(ref Vector2 position, float angle) { Debug.Assert(World != null); + if (World == null) + throw new InvalidOperationException("Could not set the transform of a body (World was null - has the body been removed?)"); if (World.IsLocked) throw new WorldLockedException("Cannot modify the transform of a body when the World is locked.");