diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index da396dad7..043ecd319 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -393,7 +393,7 @@ namespace Barotrauma } } - partial void SeverLimbJointProjSpecific(LimbJoint limbJoint) + partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound = true) { foreach (Limb limb in new Limb[] { limbJoint.LimbA, limbJoint.LimbB }) { @@ -411,6 +411,11 @@ namespace Barotrauma character.CurrentHull?.AddDecal(character.BloodDecalName, limb.WorldPosition, MathHelper.Clamp(limb.Mass, 0.5f, 2.0f)); } } + + if (playSound) + { + SoundPlayer.PlayDamageSound("Gore", 1.0f, limbJoint.LimbA.body); + } } public virtual void Draw(SpriteBatch spriteBatch, Camera cam) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs index 777bc7775..2bcb03cb3 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Health/CharacterHealth.cs @@ -96,6 +96,11 @@ namespace Barotrauma private const float UpdateDisplayedAfflictionsInterval = 0.5f; private List currentDisplayedAfflictions = new List(); + public bool MouseOnElement + { + get { return highlightedLimbIndex > -1 || GUI.MouseOn == dropItemArea; } + } + private static CharacterHealth openHealthWindow; public static CharacterHealth OpenHealthWindow { @@ -172,18 +177,16 @@ namespace Barotrauma afflictionInfoContainer = new GUIListBox(new RectTransform(new Vector2(0.7f, 0.85f), paddedInfoFrame.RectTransform, Anchor.BottomLeft)); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.08f), paddedInfoFrame.RectTransform), TextManager.Get("SuitableTreatments"), textAlignment: Alignment.TopRight); - lowSkillIndicator = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.07f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.08f) }, - TextManager.Get("LowMedicalSkillWarning"), Color.Orange, textAlignment: Alignment.Center, font: GUI.SmallFont, wrap: true) + lowSkillIndicator = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedInfoFrame.RectTransform, Anchor.TopRight), + TextManager.Get("LowMedicalSkillWarning"), Color.Orange, textAlignment: Alignment.TopRight, font: GUI.SmallFont, wrap: true) { Visible = false }; - recommendedTreatmentContainer = new GUIListBox(new RectTransform(new Vector2(0.28f, 0.5f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.15f) }) - { - Spacing = 10 - }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), paddedInfoFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.05f) }, TextManager.Get("SuitableTreatments"), textAlignment: Alignment.BottomRight); + + recommendedTreatmentContainer = new GUIListBox(new RectTransform(new Vector2(0.28f, 0.5f), paddedInfoFrame.RectTransform, Anchor.TopRight) { RelativeOffset = new Vector2(0.0f, 0.12f) }); dropItemArea = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.3f), paddedInfoFrame.RectTransform, Anchor.BottomRight) - { RelativeOffset = new Vector2(0.0f, 0.0f) }, style: null) + { RelativeOffset = new Vector2(0.02f, 0.0f) }, style: null) { ToolTip = TextManager.Get("HealthItemUseTip") }; @@ -584,9 +587,18 @@ namespace Barotrauma var affliction = GetAllAfflictions(a => a.Prefab.IndicatorLimb != LimbType.None) .OrderByDescending(a => a.DamagePerSecond) .ThenByDescending(a => a.Strength).FirstOrDefault(); - var limbHealth = GetMathingLimbHealth(affliction); - if (limbHealth != null) + if (affliction.DamagePerSecond > 0 || affliction.Strength > 0) { + var limbHealth = GetMathingLimbHealth(affliction); + if (limbHealth != null) + { + selectedLimbIndex = limbHealths.IndexOf(limbHealth); + } + } + else + { + // If no affliction is critical, select the limb which has most damage. + var limbHealth = limbHealths.OrderByDescending(l => l.TotalDamage).FirstOrDefault(); selectedLimbIndex = limbHealths.IndexOf(limbHealth); } } @@ -928,7 +940,7 @@ namespace Barotrauma { afflictionInfoContainer.Content.ClearChildren(); recommendedTreatmentContainer.Content.ClearChildren(); - + float characterSkillLevel = Character.Controlled == null ? 0.0f : Character.Controlled.GetSkillLevel("medical"); //random variance is 200% when the skill is 0 @@ -1023,14 +1035,15 @@ namespace Barotrauma { ItemPrefab item = MapEntityPrefab.Find(name: null, identifier: treatment.Key, showErrorMessages: false) as ItemPrefab; if (item == null) continue; - int slotSize = (int)(recommendedTreatmentContainer.Content.Rect.Width * 0.8f); + int slotSize = (int)(recommendedTreatmentContainer.Content.Rect.Width * 0.5f); - var itemSlot = new GUIButton(new RectTransform(new Point(slotSize), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopCenter), - text: "", style: "InventorySlotSmall") + var itemSlot = new GUIFrame(new RectTransform(new Point(recommendedTreatmentContainer.Content.Rect.Width, slotSize), recommendedTreatmentContainer.Content.RectTransform, Anchor.TopCenter), + style: "InnerGlow") { - UserData = item + UserData = item, + CanBeFocused = false }; - itemSlot.Color = ToolBox.GradientLerp(treatment.Value, Color.Red, Color.White, Color.LightGreen); + itemSlot.Color = ToolBox.GradientLerp(treatment.Value, Color.Red, Color.Orange, Color.LightGreen); Sprite itemSprite = item.InventoryIcon ?? item.sprite; Color itemColor = itemSprite == item.sprite ? item.SpriteColor : item.InventoryIconColor; diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs index 0db66467c..5e2696495 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Limb.cs @@ -296,6 +296,7 @@ namespace Barotrauma break; } } + SoundPlayer.PlayDamageSound(damageSoundType, Math.Max(damage, bleedingDamage), WorldPosition); } // Always spawn damage particles diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index ac0dc2bbd..b11b1f60b 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -555,6 +555,37 @@ namespace Barotrauma } })); + commands.Add(new Command("resetselected", "Reset selected items and structures to prefabs. Only applicable in the subeditor.", args => + { + if (Screen.Selected == GameMain.SubEditorScreen) + { + foreach (MapEntity entity in MapEntity.SelectedList) + { + if (entity is Item item) + { + item.Reset(); + } + else if (entity is Structure structure) + { + structure.Reset(); + } + } + foreach (MapEntity entity in MapEntity.SelectedList) + { + if (entity is Item item) + { + item.CreateEditingHUD(); + break; + } + else if (entity is Structure structure) + { + structure.CreateEditingHUD(); + break; + } + } + } + })); + commands.Add(new Command("alpha", "Change the alpha (as bytes from 0 to 255) of the selected item/structure instances. Applied only in the subeditor.", (string[] args) => { if (Screen.Selected == GameMain.SubEditorScreen) @@ -1051,6 +1082,130 @@ namespace Barotrauma }; })); + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => + { + string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; + List lines = new List(); + foreach (MapEntityPrefab me in MapEntityPrefab.List) + { + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); + } + File.WriteAllLines(filePath, lines); + })); +#if DEBUG + commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => + { + if (args.Length != 1) return; + TextManager.CheckForDuplicates(args[0]); + })); + + commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) => + { + TextManager.WriteToCSV(); + NPCConversation.WriteToCSV(); + })); + + commands.Add(new Command("csvtoxml", "csvtoxml [language] -> Converts .csv localization files in Content/NPCConversations & Content/Texts to .xml for use in-game.", (string[] args) => + { + if (args.Length == 0) return; + LocalizationCSVtoXML.Convert(args[0]); + })); +#endif + + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); + + GameMain.Config.GraphicsWidth = 0; + GameMain.Config.GraphicsHeight = 0; + GameMain.Config.WindowMode = WindowMode.Fullscreen; + NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green); + NewMessage("Fullscreen enabled", Color.Green); + + GameSettings.ShowUserStatisticsPrompt = true; + + GameSettings.VerboseLogging = false; + + if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster") + { + ThrowError("MasterServerUrl \"" + GameMain.Config.MasterServerUrl + "\"!"); + } + + GameMain.Config.SaveNewPlayerConfig(); + + var saveFiles = System.IO.Directory.GetFiles(SaveUtil.SaveFolder); + + foreach (string saveFile in saveFiles) + { + System.IO.File.Delete(saveFile); + NewMessage("Deleted " + saveFile, Color.Green); + } + + if (System.IO.Directory.Exists(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"))) + { + ThrowError("Reading the file \"" + sourcePath + "\" failed.", e); + return; + } + var doc = XMLExtensions.TryLoadXml(destinationPath); + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (i >= lines.Length) + { + ThrowError("Error while loading texts to the xml file. The xml has more elements than the number of lines in the text file."); + return; + } + element.Value = lines[i]; + i++; + } + doc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Select(f => f.Replace("\\", "/")); + return new string[][] + { + files.Where(f => Path.GetExtension(f)==".txt").ToArray(), + files.Where(f => Path.GetExtension(f)==".xml").ToArray() + }; + })); + + commands.Add(new Command("updatetextfile", "updatetextfile [sourcefile] [destinationfile]: Inserts all the xml elements that are only present in the source file into the destination file. Can be used to update outdated translation files more easily.", (string[] args) => + { + if (args.Length < 2) return; + string sourcePath = args[0]; + string destinationPath = args[1]; + + var sourceDoc = XMLExtensions.TryLoadXml(sourcePath); + var destinationDoc = XMLExtensions.TryLoadXml(destinationPath); + + XElement destinationElement = destinationDoc.Root.Elements().First(); + foreach (XElement element in sourceDoc.Root.Elements()) + { + if (destinationDoc.Root.Element(element.Name) == null) + { + element.Value = "!!!!!!!!!!!!!" + element.Value; + destinationElement.AddAfterSelf(element); + } + XNode nextNode = destinationElement.NextNode; + while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; + destinationElement = nextNode as XElement; + } + destinationDoc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Where(f => Path.GetExtension(f) == ".xml").Select(f => f.Replace("\\", "/")).ToArray(); + return new string[][] + { + files, + files + }; + })); + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => { string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; @@ -1694,7 +1849,7 @@ namespace Barotrauma character.AnimController.ResetRagdoll(); }, isCheat: true)); - commands.Add(new Command("reloadwearables|reloadlimbs", "Reloads the sprites of all limbs and wearable sprites (clothing) of the controlled character. Provide id or name if you want to target another character.", args => + commands.Add(new Command("reloadwearables", "Reloads the sprites of all limbs and wearable sprites (clothing) of the controlled character. Provide id or name if you want to target another character.", args => { var character = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, true); if (character == null) @@ -1705,6 +1860,26 @@ namespace Barotrauma ReloadWearables(character); }, isCheat: true)); + commands.Add(new Command("loadwearable", "Force select certain variant for the selected character.", args => + { + var character = Character.Controlled; + if (character == null) + { + ThrowError("Not controlling any character."); + return; + } + if (args.Length == 0) + { + ThrowError("No arguments provided! Give an index number for the variant starting from 1."); + return; + } + if (int.TryParse(args[0], out int variant)) + { + ReloadWearables(character, variant); + } + + }, isCheat: true)); + commands.Add(new Command("reloadsprite|reloadsprites", "Reloads the sprites of the selected item(s)/structure(s) (hovering over or selecting in the subeditor) or the controlled character. Can also reload sprites by entity id or by the name attribute (sprite element). Example 1: reloadsprite id itemid. Example 2: reloadsprite name \"Sprite name\"", args => { if (Screen.Selected is SpriteEditorScreen) @@ -1852,7 +2027,7 @@ namespace Barotrauma }, isCheat: true)); } - private static void ReloadWearables(Character character) + private static void ReloadWearables(Character character, int variant = 0) { foreach (var limb in character.AnimController.Limbs) { @@ -1861,11 +2036,17 @@ namespace Barotrauma limb.DeformSprite?.Sprite.ReloadTexture(); foreach (var wearable in limb.WearingItems) { + if (variant > 0 && wearable.Variant > 0) + { + wearable.Variant = variant; + } + wearable.RefreshPath(); wearable.Sprite.ReloadXML(); wearable.Sprite.ReloadTexture(); } foreach (var wearable in limb.OtherWearables) { + wearable.RefreshPath(); wearable.Sprite.ReloadXML(); wearable.Sprite.ReloadTexture(); } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs index 64fecb33b..490c0d21a 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/ChatBox.cs @@ -26,7 +26,21 @@ namespace Barotrauma private bool isSinglePlayer; public bool IsSinglePlayer => isSinglePlayer; - private bool toggleOpen = true; + private bool _toggleOpen = true; + public bool ToggleOpen + { + get { return _toggleOpen; } + set + { + if (_toggleOpen == value) { return; } + _toggleOpen = GameMain.Config.ChatOpen = value; + foreach (GUIComponent child in ToggleButton.Children) + { + child.SpriteEffects = _toggleOpen == (HUDLayoutSettings.ChatBoxAlignment == Alignment.Right) ? + SpriteEffects.FlipHorizontally : SpriteEffects.None; + } + } + } private float openState; private float prevUIScale; @@ -81,12 +95,7 @@ namespace Barotrauma toggleButton.OnClicked += (GUIButton btn, object userdata) => { - toggleOpen = !toggleOpen; - foreach (GUIComponent child in btn.Children) - { - child.SpriteEffects = toggleOpen == (HUDLayoutSettings.ChatBoxAlignment == Alignment.Right) ? - SpriteEffects.FlipHorizontally : SpriteEffects.None; - } + ToggleOpen = !ToggleOpen; return true; }; @@ -124,6 +133,8 @@ namespace Barotrauma } return true; }; + + ToggleOpen = GameMain.Config.ChatOpen; } public bool TypingChatMessage(GUITextBox textBox, string text) @@ -228,7 +239,7 @@ namespace Barotrauma chatBox.UpdateScrollBarSize(); - if (!toggleOpen) + if (!ToggleOpen) { var popupMsg = new GUIFrame(new RectTransform(Vector2.One, guiFrame.RectTransform), style: "GUIToolTip") { @@ -313,9 +324,7 @@ namespace Barotrauma prevUIScale = GUI.Scale; } - - - if (toggleOpen || (inputBox != null && inputBox.Selected)) + if (ToggleOpen || (inputBox != null && inputBox.Selected)) { openState += deltaTime * 5.0f; //delete all popup messages when the chatbox is open diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs index 53457108e..dddd37a0f 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUI.cs @@ -1568,7 +1568,7 @@ namespace Barotrauma } /// - /// Displays a message at the center of the screen, automatically preventing overlapping with other centered messages + /// Displays a message at the center of the screen, automatically preventing overlapping with other centered messages. TODO: Allow to show messages at the middle of the screen (instead of the top center). /// public static void AddMessage(string message, Color color, float? lifeTime = null, bool playSound = true, ScalableFont font = null) { diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index 10fa40667..51f3c76fb 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -318,8 +318,16 @@ namespace Barotrauma SoundManager.SetCategoryGainMultiplier("voip", Config.VoiceChatVolume); if (Config.EnableSplashScreen) { - (TitleScreen as LoadingScreen).SplashScreen = new Video(base.GraphicsDevice, SoundManager, "Content/splashscreen.mp4", 1280, 720); - } + try + { + (TitleScreen as LoadingScreen).SplashScreen = new Video(base.GraphicsDevice, SoundManager, "Content/splashscreen.mp4", 1280, 720); + } + catch (Exception e) + { + Config.EnableSplashScreen = false; + DebugConsole.ThrowError("Playing the splash screen failed.", e); + } + } GUI.Init(Window, Config.SelectedContentPackages, GraphicsDevice); DebugConsole.Init(); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index a4a41d1d0..1451bace0 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -41,7 +41,10 @@ namespace Barotrauma private bool toggleCrewAreaOpen = true; private int characterInfoWidth; - private ChatBox chatBox; + /// + /// Present only in single player games. In multiplayer. The chatbox is found from GameSession.Client. + /// + public ChatBox ChatBox { get; private set; } private float prevUIScale; @@ -50,7 +53,15 @@ namespace Barotrauma public bool ToggleCrewAreaOpen { get { return toggleCrewAreaOpen; } - set { toggleCrewAreaOpen = value; } + set + { + if (toggleCrewAreaOpen == value) { return; } + toggleCrewAreaOpen = GameMain.Config.CrewMenuOpen = value; + foreach (GUIComponent child in toggleCrewButton.Children) + { + child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + } + } } #endregion @@ -93,11 +104,7 @@ namespace Barotrauma "", style: "UIToggleButton"); toggleCrewButton.OnClicked += (GUIButton btn, object userdata) => { - toggleCrewAreaOpen = !toggleCrewAreaOpen; - foreach (GUIComponent child in btn.Children) - { - child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally; - } + ToggleCrewAreaOpen = !ToggleCrewAreaOpen; return true; }; @@ -125,7 +132,7 @@ namespace Barotrauma if (isSinglePlayer) { - chatBox = new ChatBox(guiFrame, isSinglePlayer: true) + ChatBox = new ChatBox(guiFrame, isSinglePlayer: true) { OnEnterMessage = (textbox, text) => { @@ -158,7 +165,7 @@ namespace Barotrauma } }; - chatBox.InputBox.OnTextChanged += chatBox.TypingChatMessage; + ChatBox.InputBox.OnTextChanged += ChatBox.TypingChatMessage; } var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); @@ -208,6 +215,8 @@ namespace Barotrauma screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); prevUIScale = GUI.Scale; + + ToggleCrewAreaOpen = GameMain.Config.CrewMenuOpen; } @@ -271,7 +280,6 @@ namespace Barotrauma DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace); return; } - } characterInfos.Add(characterInfo); } @@ -599,7 +607,20 @@ namespace Barotrauma characterListBox.BarScroll = roundedPos; } - return false; + #region Dialog + /// + /// Adds the message to the single player chatbox. + /// + public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Character sender) + { + if (!isSinglePlayer) + { + DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace); + return; + } + if (string.IsNullOrEmpty(text)) { return; } + + chatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender)); } private IEnumerable KillCharacterAnim(GUIComponent component) @@ -612,6 +633,12 @@ namespace Barotrauma { comp.Color = Color.DarkRed; } + List availableSpeakers = Character.CharacterList.FindAll(c => + c.AIController is HumanAIController && + !c.IsDead && + c.SpeechImpediment <= 100.0f); + pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers)); + } yield return new WaitForSeconds(1.0f); @@ -647,7 +674,7 @@ namespace Barotrauma } if (string.IsNullOrEmpty(text)) { return; } - chatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender)); + ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender)); } private WifiComponent GetHeadset(Character character, bool requireEquipped) @@ -723,7 +750,7 @@ namespace Barotrauma if (IsSinglePlayer) { orderGiver.Speak( - order.GetChatMessage("", orderGiver.CurrentHull?.RoomName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order); + order.GetChatMessage("", orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver), ChatMessageType.Order); } else { @@ -740,7 +767,7 @@ namespace Barotrauma if (IsSinglePlayer) { orderGiver?.Speak( - order.GetChatMessage(character.Name, orderGiver.CurrentHull?.RoomName, givingOrderToSelf: character == orderGiver, orderOption: option), null); + order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null); } else if (orderGiver != null) { @@ -1057,24 +1084,24 @@ namespace Barotrauma } if (GUI.DisableHUD || GUI.DisableUpperHUD) return; - if (chatBox != null) + if (ChatBox != null) { - chatBox.Update(deltaTime); - chatBox.InputBox.Visible = Character.Controlled != null; + ChatBox.Update(deltaTime); + ChatBox.InputBox.Visible = Character.Controlled != null; - if (!DebugConsole.IsOpen && chatBox.InputBox.Visible) + if (!DebugConsole.IsOpen && ChatBox.InputBox.Visible) { - if (PlayerInput.KeyHit(InputType.Chat) && !chatBox.InputBox.Selected) + if (PlayerInput.KeyHit(InputType.Chat) && !ChatBox.InputBox.Selected) { - chatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f); - chatBox.InputBox.Select(); + ChatBox.GUIFrame.Flash(Color.DarkGreen, 0.5f); + ChatBox.InputBox.Select(); } - if (PlayerInput.KeyHit(InputType.RadioChat) && !chatBox.InputBox.Selected) + if (PlayerInput.KeyHit(InputType.RadioChat) && !ChatBox.InputBox.Selected) { - chatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f); - chatBox.InputBox.Select(); - chatBox.InputBox.Text = "r; "; + ChatBox.GUIFrame.Flash(Color.YellowGreen, 0.5f); + ChatBox.InputBox.Select(); + ChatBox.InputBox.Text = "r; "; } } } @@ -1144,7 +1171,7 @@ namespace Barotrauma crewArea.RectTransform.AbsoluteOffset = Vector2.SmoothStep(new Vector2(-crewArea.Rect.Width, 0), new Vector2(toggleCrewButton.Rect.Width, 0), crewAreaOpenState).ToPoint(); - crewAreaOpenState = toggleCrewAreaOpen ? + crewAreaOpenState = ToggleCrewAreaOpen ? Math.Min(crewAreaOpenState + deltaTime * 2.0f, 1.0f) : Math.Max(crewAreaOpenState - deltaTime * 2.0f, 0.0f); @@ -1157,7 +1184,7 @@ namespace Barotrauma { Character.Controlled.SelectedConstruction = null; } - toggleCrewAreaOpen = !toggleCrewAreaOpen; + ToggleCrewAreaOpen = !ToggleCrewAreaOpen; } UpdateReports(deltaTime); @@ -1285,7 +1312,7 @@ namespace Barotrauma { reportButtonFrame.Visible = true; - var reportButtonParent = chatBox ?? GameMain.Client.ChatBox; + var reportButtonParent = ChatBox ?? GameMain.Client.ChatBox; 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); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs index 8ec219fd1..bb18197ea 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -135,6 +135,8 @@ namespace Barotrauma msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex); msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex); + msg.Write(PurchasedHullRepairs); + msg.Write(PurchasedItemRepairs); msg.Write((UInt16)CargoManager.PurchasedItems.Count); foreach (PurchasedItem pi in CargoManager.PurchasedItems) diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index 710d858e7..cf301e67d 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -81,7 +81,8 @@ namespace Barotrauma var leftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1.0f), settingsFramePadding.RectTransform, Anchor.TopLeft)); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftPanel.RectTransform), - TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont); + TextManager.Get("Settings"), textAlignment: Alignment.TopLeft, font: GUI.LargeFont) + { ForceUpperCase = true }; var generalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), leftPanel.RectTransform, Anchor.TopLeft)); @@ -438,6 +439,17 @@ namespace Barotrauma DebugConsole.NewMessage(name + " " + name.Length.ToString(), Color.Lime); } + GUITickBox directionalVoiceChat = new GUITickBox(new RectTransform(new Point(32, 32), audioSliders.RectTransform), TextManager.Get("DirectionalVoiceChat")); + directionalVoiceChat.Selected = UseDirectionalVoiceChat; + directionalVoiceChat.ToolTip = TextManager.Get("DirectionalVoiceChatToolTip"); + directionalVoiceChat.OnSelected = (tickBox) => + { + UseDirectionalVoiceChat = tickBox.Selected; + UnsavedSettings = true; + return true; + }; + + if (string.IsNullOrWhiteSpace(VoiceCaptureDevice)) VoiceCaptureDevice = deviceNames[0]; #if (!OSX) var deviceList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), audioSliders.RectTransform), VoiceCaptureDevice, deviceNames.Count); diff --git a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs index 6d4cb8ef3..aaa4cf5b2 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs @@ -19,7 +19,7 @@ namespace Barotrauma Right, Center } - + private enum QuickUseAction { None, @@ -34,6 +34,8 @@ namespace Barotrauma } private static Dictionary limbSlotIcons; + + const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; private Point screenResolution; @@ -51,11 +53,42 @@ namespace Barotrauma } } public bool Hidden { get; set; } + + private bool hidePersonalSlots; + private float hidePersonalSlotsState; + private GUIButton hideButton; + private Rectangle personalSlotArea; + public bool HidePersonalSlots + { + get { return hidePersonalSlots; } + } + + public Rectangle PersonalSlotArea + { + get { return personalSlotArea; } + } + partial void InitProjSpecific(XElement element) { Hidden = true; + hideButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), (int)(60 * GUI.Scale)), GUI.Canvas) + { AbsoluteOffset = HUDLayoutSettings.CrewArea.Location }, + "", style: "UIToggleButton"); + hideButton.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally); + hideButton.OnClicked += (GUIButton btn, object userdata) => + { + hidePersonalSlots = !hidePersonalSlots; + foreach (GUIComponent child in btn.Children) + { + child.SpriteEffects = hidePersonalSlots ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + } + return true; + }; + + hidePersonalSlots = false; + if (limbSlotIcons == null) { limbSlotIcons = new Dictionary(); @@ -68,12 +101,11 @@ namespace Barotrauma limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/IconAtlas.png", new Rectangle(640 + margin, 383 + margin, 128 - margin * 2, 128 - margin * 2))); limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/IconAtlas.png", new Rectangle(768 + margin, 383 + margin, 128 - margin * 2, 128 - margin * 2))); } - SlotPositions = new Vector2[SlotTypes.Length]; CurrentLayout = Layout.Default; SetSlotPositions(layout); } - + protected override void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true) { base.PutItem(item, i, user, removeItem, createNetworkEvent); @@ -159,6 +191,11 @@ namespace Barotrauma { if (slots[i].Disabled || (hideEmptySlot[i] && Items[i] == null)) return true; + if (layout == Layout.Default) + { + if (PersonalSlots.HasFlag(SlotTypes[i]) && !personalSlotArea.Contains(slots[i].Rect.Center + slots[i].DrawOffset.ToPoint())) return true; + } + //no need to draw the right hand slot if the item is in both hands if (Items[i] != null && SlotTypes[i] == InvSlotType.RightHand && IsInLimbSlot(Items[i], InvSlotType.LeftHand)) { @@ -176,7 +213,6 @@ namespace Barotrauma return false; } - private void SetSlotPositions(Layout layout) { int spacing = (int)(10 * UIScale); @@ -185,27 +221,32 @@ namespace Barotrauma if (slots == null) CreateSlots(); - var upperSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.Head; + hideButton.Visible = false; switch (layout) { case Layout.Default: { - int personalSlotCount = SlotTypes.Count(s => upperSlots.HasFlag(s)); - int normalSlotCount = SlotTypes.Count(s => !upperSlots.HasFlag(s)); + int personalSlotCount = SlotTypes.Count(s => PersonalSlots.HasFlag(s)); + int normalSlotCount = SlotTypes.Count(s => !PersonalSlots.HasFlag(s)); int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (slotSize.X + spacing) / 2; - int upperX = HUDLayoutSettings.PortraitArea.X - slotSize.X; + int upperX = HUDLayoutSettings.PortraitArea.X - slotSize.X * 2; //make sure the rightmost normal slot doesn't overlap with the personal slots 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 (upperSlots.HasFlag(SlotTypes[i])) + if (PersonalSlots.HasFlag(SlotTypes[i])) { 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 { @@ -213,19 +254,29 @@ namespace Barotrauma x += slotSize.X + spacing; } } + + if (hideButtonSlotIndex > -1) + { + hideButton.RectTransform.SetPosition(Anchor.TopLeft, Pivot.TopLeft); + hideButton.RectTransform.NonScaledSize = new Point(slotSize.X / 2, slotSize.Y + slots[hideButtonSlotIndex].EquipButtonRect.Height); + hideButton.RectTransform.AbsoluteOffset = new Point( + personalSlotArea.Right + spacing, + personalSlotArea.Y - slots[hideButtonSlotIndex].EquipButtonRect.Height); + hideButton.Visible = true; + } } break; case Layout.Right: { int extraOffset = 0; int x = HUDLayoutSettings.InventoryAreaLower.Right; - int upperX = HUDLayoutSettings.InventoryAreaLower.Right; + int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - slotSize.X - spacing; for (int i = 0; i < slots.Length; i++) { if (HideSlot(i)) continue; - if (upperSlots.HasFlag(SlotTypes[i])) + if (PersonalSlots.HasFlag(SlotTypes[i])) { - upperX -= slotSize.X + spacing; + //upperX -= slotSize.X + spacing; } else { @@ -237,10 +288,10 @@ namespace Barotrauma for (int i = 0; i < SlotPositions.Length; i++) { if (HideSlot(i)) continue; - if (upperSlots.HasFlag(SlotTypes[i])) + if (PersonalSlots.HasFlag(SlotTypes[i])) { - SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - spacing * 2); - upperX += slots[i].Rect.Width + spacing; + SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - extraOffset - spacing * 2); + personalSlotX -= slots[i].Rect.Width + spacing; } else { @@ -261,14 +312,14 @@ namespace Barotrauma case Layout.Left: { int x = HUDLayoutSettings.InventoryAreaLower.X; - int upperX = x; + int personalSlotX = x; for (int i = 0; i < SlotPositions.Length; i++) { if (HideSlot(i)) continue; - if (upperSlots.HasFlag(SlotTypes[i])) + if (PersonalSlots.HasFlag(SlotTypes[i])) { - SlotPositions[i] = new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2); - upperX += slots[i].Rect.Width + spacing; + SlotPositions[i] = new Vector2(personalSlotX, GameMain.GraphicsHeight - bottomOffset * 2 - spacing * 2); + personalSlotX += slots[i].Rect.Width + spacing; } else { @@ -354,6 +405,27 @@ namespace Barotrauma ((selectedSlot != null && selectedSlot.IsSubSlot) || (draggingItem != null && (draggingSlot == null || !draggingSlot.MouseOn()))); if (CharacterHealth.OpenHealthWindow != null) hoverOnInventory = true; + if (layout == Layout.Default && hideButton.Visible) + { + hideButton.AddToGUIUpdateList(); + hideButton.UpdateManually(deltaTime, alsoChildren: true); + + hidePersonalSlotsState = hidePersonalSlots ? + Math.Min(hidePersonalSlotsState + deltaTime * 5.0f, 1.0f) : + Math.Max(hidePersonalSlotsState - deltaTime * 5.0f, 0.0f); + + for (int i = 0; i < slots.Length; i++) + { + if (!PersonalSlots.HasFlag(SlotTypes[i])) { continue; } + if (HidePersonalSlots) + { + if (selectedSlot?.Slot == slots[i]) { selectedSlot = null; } + highlightedSubInventorySlots.RemoveWhere(s => s.Slot == slots[i]); + } + slots[i].DrawOffset = Vector2.Lerp(Vector2.Zero, new Vector2(personalSlotArea.Width, 0.0f), hidePersonalSlotsState); + } + } + if (hoverOnInventory) HideTimer = 0.5f; if (HideTimer > 0.0f) HideTimer -= deltaTime; @@ -366,6 +438,32 @@ namespace Barotrauma QuickUseItem(Items[i], true, false, true); } } + } + + //force personal slots open if an item is running out of battery/fuel/oxygen/etc + if (hidePersonalSlots) + { + for (int i = 0; i < slots.Length; i++) + { + if (Items[i]?.OwnInventory != null && Items[i].OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i])) + { + if (Items[i].OwnInventory.Items[0].Condition > 0.0f && + Items[i].OwnInventory.Items[0].Condition / Items[i].OwnInventory.Items[0].MaxCondition < 0.15f) + { + hidePersonalSlots = false; + } + } + } + } + + List hideSubInventories = new List(); + foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) + { + if (highlightedSubInventorySlot.ParentInventory == this) + { + UpdateSubInventory(deltaTime, highlightedSubInventorySlot.SlotIndex, cam); + } + } List hideSubInventories = new List(); foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) @@ -704,7 +802,12 @@ namespace Barotrauma } base.Draw(spriteBatch); - + + if (hideButton != null && hideButton.Visible) + { + hideButton.DrawManually(spriteBatch, alsoChildren: true); + } + InventorySlot highlightedQuickUseSlot = null; for (int i = 0; i < capacity; i++) { @@ -718,7 +821,7 @@ namespace Barotrauma if (limbSlotIcons.ContainsKey(SlotTypes[i])) { var icon = limbSlotIcons[SlotTypes[i]]; - icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2(), Color.White * 0.3f, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X); + icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2() + slots[i].DrawOffset, Color.White * 0.3f, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X); } continue; } @@ -728,12 +831,12 @@ namespace Barotrauma if (IsInLimbSlot(Items[i], InvSlotType.LeftHand)) { var icon = limbSlotIcons[InvSlotType.LeftHand]; - icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom), 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); + 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), 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); + 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); } Color color = slots[i].EquipButtonState == GUIComponent.ComponentState.Pressed ? Color.Gray : Color.White * 0.8f; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs index 1bd8174d6..d2e5c4a67 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -377,9 +377,13 @@ namespace Barotrauma.Items.Components public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { } - public ItemComponent GetLinkUIToComponent() + public virtual void CreateEditingHUD(SerializableEntityEditor editor) { - if (string.IsNullOrEmpty(LinkUIToComponent)) + } + + private bool LoadElemProjSpecific(XElement subElement) + { + switch (subElement.Name.ToString().ToLowerInvariant()) { case "guiframe": if (subElement.Attribute("rect") != null) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemLabel.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemLabel.cs index 5bdd103f3..10eb66e95 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemLabel.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemLabel.cs @@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components } private string text; - [Serialize("", true), Editable(100)] + [Serialize("", true, translationTextTag: "Label."), Editable(100)] public string Text { get { return text; } @@ -40,9 +40,10 @@ namespace Barotrauma.Items.Components { textBlock = null; } - + text = value; - TextBlock.Text = value; + DisplayText = TextManager.Get(text, returnNull: true) ?? value; + TextBlock.Text = DisplayText; SetScrollingText(); } } @@ -121,7 +122,7 @@ namespace Barotrauma.Items.Components { if (!scrollable) return; - float totalWidth = textBlock.Font.MeasureString(text).X; + float totalWidth = textBlock.Font.MeasureString(DisplayText).X; float textAreaWidth = Math.Max(textBlock.Rect.Width - textBlock.Padding.X - textBlock.Padding.Z, 0); if (totalWidth >= textAreaWidth) { @@ -129,13 +130,13 @@ namespace Barotrauma.Items.Components //(so the text can scroll entirely out of view before we reset it back to start) needsScrolling = true; float spaceWidth = textBlock.Font.MeasureChar(' ').X; - scrollingText = new string(' ', (int)Math.Ceiling(textAreaWidth / spaceWidth)) + text; + scrollingText = new string(' ', (int)Math.Ceiling(textAreaWidth / spaceWidth)) + DisplayText; } else { //whole text can fit in the textblock, no need to scroll needsScrolling = false; - scrollingText = text; + scrollingText = DisplayText; scrollAmount = 0.0f; scrollIndex = 0; return; @@ -151,7 +152,7 @@ namespace Barotrauma.Items.Components charWidths[i] = charWidth; } - scrollIndex = MathHelper.Clamp(scrollIndex, 0, text.Length); + scrollIndex = MathHelper.Clamp(scrollIndex, 0, DisplayText.Length); } public override void Update(float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs index cd5c74d3e..5967624a1 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/MiniMap.cs @@ -221,7 +221,7 @@ namespace Barotrauma.Items.Components { hullInfoFrame.RectTransform.ScreenSpaceOffset = hullFrame.Rect.Center; hullInfoFrame.Visible = true; - hullNameText.Text = hull.RoomName; + hullNameText.Text = hull.DisplayName; foreach (Hull linkedHull in hullData.LinkedHulls) { diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs index 9cc203358..6a8ee3d51 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Sonar.cs @@ -785,15 +785,32 @@ namespace Barotrauma.Items.Components foreach (Character c in Character.CharacterList) { - if (c.AnimController.CurrentHull != null || !c.Enabled) continue; - if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue; + if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; } + if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; } + + if (c.AnimController.SimplePhysicsEnabled) + { + float pointDist = ((c.WorldPosition - pingSource) * displayScale).LengthSquared(); + if (pointDist > DisplayRadius * DisplayRadius) { continue; } + + if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) + { + var blip = new SonarBlip( + c.WorldPosition, + MathHelper.Clamp(c.Mass, 0.1f, pingStrength), + MathHelper.Clamp(c.Mass * 0.03f, 0.1f, 2.0f)); + if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } + sonarBlips.Add(blip); + } + continue; + } foreach (Limb limb in c.AnimController.Limbs) { if (!limb.body.Enabled) { continue; } float pointDist = ((limb.WorldPosition - pingSource) * displayScale).LengthSquared(); - if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) continue; + if (limb.SimPosition == Vector2.Zero || pointDist > DisplayRadius * DisplayRadius) { continue; } if (pointDist > prevPingRadiusSqr && pointDist < pingRadiusSqr) { @@ -801,13 +818,13 @@ namespace Barotrauma.Items.Components limb.WorldPosition + Rand.Vector(limb.Mass / 10.0f), MathHelper.Clamp(limb.Mass, 0.1f, pingStrength), MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f)); - if (!passive && !CheckBlipVisibility(blip, transducerPos)) continue; + if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); } } } } - + private void CreateBlipsForLine(Vector2 point1, Vector2 point2, Vector2 pingSource, Vector2 transducerPos, float pingRadius, float prevPingRadius, float lineStep, float zStep, float range, float pingStrength, bool passive) { diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/CustomInterface.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/CustomInterface.cs index a74b5d3b8..fe1fbc065 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/CustomInterface.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Signal/CustomInterface.cs @@ -3,6 +3,7 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Xml.Linq; @@ -16,16 +17,22 @@ namespace Barotrauma.Items.Components { uiElements.Clear(); + var visibleElements = customInterfaceElementList.Where(ciElement => !string.IsNullOrEmpty(ciElement.Label)); + GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), GuiFrame.RectTransform, Anchor.Center), childAnchor: customInterfaceElementList.Count > 1 ? Anchor.TopCenter : Anchor.Center) - { RelativeSpacing = 0.05f }; + { + RelativeSpacing = 0.05f, + Stretch = visibleElements.Count() > 2 + }; - float elementSize = Math.Min(1.0f / customInterfaceElementList.Count, 0.5f); - foreach (CustomInterfaceElement ciElement in customInterfaceElementList) + float elementSize = Math.Min(1.0f / visibleElements.Count(), 0.5f); + foreach (CustomInterfaceElement ciElement in visibleElements) { if (ciElement.ContinuousSignal) { - var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementSize), paddedFrame.RectTransform), ciElement.Label) + var tickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, elementSize), paddedFrame.RectTransform), + TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label) { UserData = ciElement }; @@ -45,7 +52,8 @@ namespace Barotrauma.Items.Components } else { - var btn = new GUIButton(new RectTransform(new Vector2(1.0f, elementSize), paddedFrame.RectTransform), ciElement.Label, style: "GUIButtonLarge") + var btn = new GUIButton(new RectTransform(new Vector2(1.0f, elementSize), paddedFrame.RectTransform), + TextManager.Get(ciElement.Label, returnNull: true) ?? ciElement.Label, style: "GUIButtonLarge") { UserData = ciElement }; @@ -66,6 +74,24 @@ namespace Barotrauma.Items.Components } } + public override void CreateEditingHUD(SerializableEntityEditor editor) + { + 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]), + customInterfaceElementList[i].Label, "Label #" + (i + 1), ""); + editor.CreateStringField(customInterfaceElementList[i], + new SerializableProperty(signalProperty, customInterfaceElementList[i]), + customInterfaceElementList[i].Signal, "Signal #" + (i + 1), ""); + } + } + partial void UpdateLabelsProjSpecific() { for (int i = 0; i < labels.Length && i < uiElements.Count; i++) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs index 9f6de7fde..30eaf8155 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs @@ -154,8 +154,6 @@ namespace Barotrauma public SlotReference(Inventory parentInventory, InventorySlot slot, int slotIndex, bool isSubSlot, Inventory subInventory = null) { - - ParentInventory = parentInventory; Slot = slot; SlotIndex = slotIndex; @@ -714,12 +712,15 @@ namespace Barotrauma float scale = Math.Min(Math.Min(iconSize / sprite.size.X, iconSize / sprite.size.Y), 1.5f); Vector2 itemPos = PlayerInput.MousePosition; - if (GUI.MouseOn == null && selectedSlot == null) + bool mouseOnHealthInterface = CharacterHealth.OpenHealthWindow != null && CharacterHealth.OpenHealthWindow.MouseOnElement; + + if ((GUI.MouseOn == null || mouseOnHealthInterface) && selectedSlot == null) { var shadowSprite = GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0]; - string toolTip = Character.Controlled.FocusedItem != null ? - TextManager.Get("PutItemIn").Replace("[itemname]", Character.Controlled.FocusedItem.Name) : - TextManager.Get("DropItem"); + string toolTip = mouseOnHealthInterface ? TextManager.Get("QuickUseAction.UseTreatment") : + Character.Controlled.FocusedItem != null ? + TextManager.Get("PutItemIn").Replace("[itemname]", Character.Controlled.FocusedItem.Name) : + TextManager.Get("DropItem"); int textWidth = (int)Math.Max(GUI.Font.MeasureString(draggingItem.Name).X, GUI.SmallFont.MeasureString(toolTip).X); int textSpacing = (int)(15 * GUI.Scale); Point shadowBorders = (new Point(40, 10)).Multiply(GUI.Scale); @@ -727,7 +728,7 @@ namespace Barotrauma new Rectangle(itemPos.ToPoint() - new Point(iconSize / 2) - shadowBorders, new Point(iconSize + textWidth + textSpacing, iconSize) + shadowBorders.Multiply(2)), Color.Black * 0.8f); GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y - iconSize / 2), draggingItem.Name, Color.White); GUI.DrawString(spriteBatch, new Vector2(itemPos.X + iconSize / 2 + textSpacing, itemPos.Y), toolTip, - color: Character.Controlled.FocusedItem == null ? Color.Red : Color.LightGreen, + color: Character.Controlled.FocusedItem == null && !mouseOnHealthInterface ? Color.Red : Color.LightGreen, font: GUI.SmallFont); } sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black, scale: scale); @@ -853,7 +854,7 @@ namespace Barotrauma if (itemContainer.ContainedStateIndicator?.Texture == null) { containedIndicatorArea.Inflate(0, -2); - GUI.DrawRectangle(spriteBatch, containedIndicatorArea, Color.DarkGray * 0.8f, true); + GUI.DrawRectangle(spriteBatch, containedIndicatorArea, Color.DarkGray * 0.9f, true); GUI.DrawRectangle(spriteBatch, new Rectangle(containedIndicatorArea.X, containedIndicatorArea.Y, (int)(containedIndicatorArea.Width * containedState), containedIndicatorArea.Height), Color.Lerp(Color.Red, Color.Green, containedState) * 0.8f, true); @@ -867,11 +868,11 @@ namespace Barotrauma if (containedState > 0.0f && containedState < 0.25f) { - indicatorScale += ((float)Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) * 0.1f; + indicatorScale += ((float)Math.Sin(Timing.TotalTime * 5.0f) + 1.0f) * 0.25f; } indicatorSprite.Draw(spriteBatch, containedIndicatorArea.Center.ToVector2(), - Color.DarkGray * 0.6f, + Color.DarkGray * 0.9f, origin: indicatorSprite.size / 2, rotate: 0.0f, scale: indicatorScale); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index fe938dafd..c0e0029d7 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -41,8 +41,6 @@ namespace Barotrauma { get { return activeSprite; } } - - public float SpriteRotation; private GUITextBlock itemInUseWarning; private GUITextBlock ItemInUseWarning @@ -103,9 +101,22 @@ namespace Barotrauma return color; } - partial void SetActiveSprite() + partial void SetActiveSpriteProjSpecific() { activeSprite = prefab.sprite; + Holdable holdable = GetComponent(); + if (holdable != null && holdable.Attached) + { + foreach (ContainedItemSprite containedSprite in Prefab.ContainedSprites) + { + if (containedSprite.UseWhenAttached) + { + activeSprite = containedSprite.Sprite; + return; + } + } + } + if (Container != null) { foreach (ContainedItemSprite containedSprite in Prefab.ContainedSprites) @@ -175,8 +186,7 @@ namespace Barotrauma Color color = isHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? Color.Orange : GetSpriteColor(); //if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f); - - Sprite activeSprite = prefab.sprite; + BrokenItemSprite fadeInBrokenSprite = null; float fadeInBrokenSpriteAlpha = 0.0f; if (condition < Prefab.Health) @@ -567,8 +577,13 @@ namespace Barotrauma } var componentEditor = new SerializableEntityEditor(listBox.Content.RectTransform, ic, inGame, showName: !inGame); - - if (inGame) continue; + + if (inGame) + { + ic.CreateEditingHUD(componentEditor); + componentEditor.Recalculate(); + continue; + } foreach (var kvp in ic.requiredItems) { @@ -602,6 +617,10 @@ namespace Barotrauma } } + ic.CreateEditingHUD(componentEditor); + componentEditor.Recalculate(); + } + PositionEditingHUD(); SetHUDLayout(); @@ -784,7 +803,7 @@ namespace Barotrauma { if (!ic.CanBeSelected) { continue; } - bool useAlternativeLayout = ic.Item != this; + bool useAlternativeLayout = activeHUDs.Count > 1; bool wasUsingAlternativeLayout = ic.UseAlternativeLayout; ic.UseAlternativeLayout = useAlternativeLayout; needsLayoutUpdate |= ic.UseAlternativeLayout != wasUsingAlternativeLayout; @@ -818,14 +837,28 @@ namespace Barotrauma case NetEntityEvent.Type.ComponentState: { int componentIndex = msg.ReadRangedInteger(0, components.Count - 1); - (components[componentIndex] as IServerSerializable).ClientRead(type, msg, sendingTime); + if (components[componentIndex] is IServerSerializable serverSerializable) + { + serverSerializable.ClientRead(type, msg, sendingTime); + } + else + { + throw new Exception("Failed to read component state - " + components[componentIndex].GetType() + " is not IServerSerializable."); + } } break; - + case NetEntityEvent.Type.InventoryState: - { + { int containerIndex = msg.ReadRangedInteger(0, components.Count - 1); - (components[containerIndex] as ItemContainer).Inventory.ClientRead(type, msg, sendingTime); + if (components[containerIndex] is ItemContainer container) + { + container.Inventory.ClientRead(type, msg, sendingTime); + } + else + { + throw new Exception("Failed to read inventory state - " + components[containerIndex].GetType() + " is not an ItemContainer."); + } } break; case NetEntityEvent.Type.Status: diff --git a/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs b/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs index 53cdf7c63..76a9c235f 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/ItemPrefab.cs @@ -25,12 +25,14 @@ namespace Barotrauma class ContainedItemSprite { public readonly Sprite Sprite; + public readonly bool UseWhenAttached; public readonly string[] AllowedContainerIdentifiers; public readonly string[] AllowedContainerTags; public ContainedItemSprite(XElement element, string path = "", bool lazyLoad = false) { Sprite = new Sprite(element, path, lazyLoad: lazyLoad); + UseWhenAttached = element.GetAttributeBool("usewhenattached", false); AllowedContainerIdentifiers = element.GetAttributeStringArray("allowedcontaineridentifiers", new string[0], convertToLowerInvariant: true); AllowedContainerTags = element.GetAttributeStringArray("allowedcontainertags", new string[0], convertToLowerInvariant: true); } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelRenderer.cs b/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelRenderer.cs index f17838929..28a1f7b89 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelRenderer.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelRenderer.cs @@ -217,7 +217,7 @@ namespace Barotrauma spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, - SamplerState.LinearClamp, DepthStencilState.Default, null, null, + SamplerState.LinearClamp, DepthStencilState.DepthRead, null, null, cam.Transform); if (backgroundSpriteManager != null) backgroundSpriteManager.DrawObjects(spriteBatch, cam, drawFront: true); spriteBatch.End(); diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs index a1fb2e137..5ce895e7b 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs @@ -291,11 +291,6 @@ namespace Barotrauma.Lights spriteBatch.Draw(HighlightMap, Vector2.Zero, Color.White); spriteBatch.End(); } - GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive); - spriteBatch.End(); - - //draw a black rectangle on hulls to hide background lights behind subs - //--------------------------------------------------------------------------------------------------- //draw characters to obstruct the highlighted items/characters and light sprites //--------------------------------------------------------------------------------------------------- @@ -500,6 +495,8 @@ namespace Barotrauma.Lights spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0, (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.White); } + spriteBatch.End(); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: SolidColorEffect, transformMatrix: spriteBatchTransform); foreach (Character c in Character.CharacterList) { if (c.Enabled) { c.Draw(spriteBatch, cam); } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs index 69c09b594..6bf1f97f8 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs @@ -215,23 +215,24 @@ namespace Barotrauma { if (Prefab.BackgroundSprite != null) { - bool drawDropShadow = Submarine != null && HasBody; Vector2 dropShadowOffset = Vector2.Zero; - if (drawDropShadow) + if (UseDropShadow) { - dropShadowOffset = Submarine.HiddenSubPosition - Position; - if (dropShadowOffset != Vector2.Zero) + dropShadowOffset = DropShadowOffset; + if (dropShadowOffset == Vector2.Zero) { - if (IsHorizontal) + if (Submarine == null) { - dropShadowOffset = new Vector2(0.0f, Math.Sign(dropShadowOffset.Y) * 10.0f); + dropShadowOffset = Vector2.UnitY * 10.0f; } else { - dropShadowOffset = new Vector2(Math.Sign(dropShadowOffset.X) * 10.0f, 0.0f); + dropShadowOffset = IsHorizontal ? + new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) : + new Vector2(Math.Sign(Submarine.HiddenSubPosition.X - Position.X) * 10.0f, 0.0f); } - dropShadowOffset.Y = -dropShadowOffset.Y; } + dropShadowOffset.Y = -dropShadowOffset.Y; } if (DrawTiled) @@ -251,7 +252,7 @@ namespace Barotrauma textureScale: TextureScale * Scale, startOffset: backGroundOffset); - if (drawDropShadow) + if (UseDropShadow) { Prefab.BackgroundSprite.DrawTiled( spriteBatch, @@ -276,7 +277,7 @@ namespace Barotrauma rotate: 0, spriteEffect: SpriteEffects); - if (drawDropShadow) + if (UseDropShadow) { Prefab.BackgroundSprite.Draw( spriteBatch, diff --git a/Barotrauma/BarotraumaClient/Source/Networking/ChatMessage.cs b/Barotrauma/BarotraumaClient/Source/Networking/ChatMessage.cs index 8b80fc766..4a560e126 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/ChatMessage.cs @@ -61,7 +61,7 @@ namespace Barotrauma.Networking { orderOption = order.Options[optionIndex]; } - txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption); + txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption); if (order.TargetAllCharacters) { diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Client.cs b/Barotrauma/BarotraumaClient/Source/Networking/Client.cs index 9d69ff7ce..55bc409f0 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Client.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Client.cs @@ -39,25 +39,33 @@ namespace Barotrauma.Networking public void UpdateSoundPosition() { - if (VoipSound != null) + if (VoipSound == null) { return; } + + if (!VoipSound.IsPlaying) { - if (!VoipSound.IsPlaying) - { - DebugConsole.Log("Destroying voipsound"); - VoipSound.Dispose(); - VoipSound = null; - return; - } + DebugConsole.Log("Destroying voipsound"); + VoipSound.Dispose(); + VoipSound = null; + return; + } - if (character != null) + if (character != null) + { + if (GameMain.Config.UseDirectionalVoiceChat) { VoipSound.SetPosition(new Vector3(character.WorldPosition.X, character.WorldPosition.Y, 0.0f)); } else { VoipSound.SetPosition(null); + float dist = Vector3.Distance(new Vector3(character.WorldPosition, 0.0f), GameMain.SoundManager.ListenerPosition); + VoipSound.Gain = 1.0f - MathUtils.InverseLerp(VoipSound.Near, VoipSound.Far, dist); } } + else + { + VoipSound.SetPosition(null); + } } partial void InitProjSpecific() diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index 81e786f93..fa4a2d3b4 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -1114,7 +1114,9 @@ namespace Barotrauma.Networking if (campaign == null) { - GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, missionIndex < 0 ? null : MissionPrefab.List[missionIndex]); + GameMain.GameSession = missionIndex < 0 ? + new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionType.None) : + new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, MissionPrefab.List[missionIndex]); GameMain.GameSession.StartRound(levelSeed, levelDifficulty, loadSecondSub); } else @@ -1126,6 +1128,18 @@ namespace Barotrauma.Networking mirrorLevel: campaign.Map.CurrentLocation != campaign.Map.SelectedConnection.Locations[0]); } + for (int i = 0; i < Submarine.MainSubs.Length; i++) + { + if (!loadSecondSub && i > 0) { break; } + + var teamID = i == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2; + Submarine.MainSubs[i].TeamID = teamID; + foreach (Submarine sub in Submarine.MainSubs[i].DockedTo) + { + sub.TeamID = teamID; + } + } + 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 + ")."; @@ -2246,7 +2260,47 @@ namespace Barotrauma.Networking public virtual void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { - if (!gameStarted || Screen.Selected != GameMain.GameScreen || GUI.DisableHUD || GUI.DisableUpperHUD) return; + if (GUI.DisableHUD || GUI.DisableUpperHUD) return; + + if (fileReceiver != null && fileReceiver.ActiveTransfers.Count > 0) + { + Vector2 downloadBarSize = new Vector2(250, 35) * GUI.Scale; + Vector2 pos = new Vector2(GameMain.NetLobbyScreen.InfoFrame.Rect.X, GameMain.GraphicsHeight - downloadBarSize.Y - 5); + + GUI.DrawRectangle(spriteBatch, new Rectangle( + (int)pos.X, + (int)pos.Y, + (int)(fileReceiver.ActiveTransfers.Count * (downloadBarSize.X + 10)), + (int)downloadBarSize.Y), + Color.Black * 0.8f, true); + + for (int i = 0; i < fileReceiver.ActiveTransfers.Count; i++) + { + var transfer = fileReceiver.ActiveTransfers[i]; + + GUI.DrawString(spriteBatch, + pos, + ToolBox.LimitString(TextManager.Get("DownloadingFile").Replace("[filename]", transfer.FileName), GUI.SmallFont, (int)downloadBarSize.X), + Color.White, null, 0, GUI.SmallFont); + GUI.DrawProgressBar(spriteBatch, new Vector2(pos.X, -pos.Y - downloadBarSize.Y / 2), new Vector2(downloadBarSize.X * 0.7f, downloadBarSize.Y / 2), transfer.Progress, Color.Green); + GUI.DrawString(spriteBatch, pos + new Vector2(5, downloadBarSize.Y / 2), + MathUtils.GetBytesReadable((long)transfer.Received) + " / " + MathUtils.GetBytesReadable((long)transfer.FileSize), + Color.White, null, 0, GUI.SmallFont); + + if (GUI.DrawButton(spriteBatch, new Rectangle( + (int)(pos.X + downloadBarSize.X * 0.7f), (int)(pos.Y + downloadBarSize.Y / 2), + (int)(downloadBarSize.X * 0.3f), (int)(downloadBarSize.Y / 2)), + TextManager.Get("Cancel"), new Color(0.47f, 0.13f, 0.15f, 0.08f))) + { + CancelFileTransfer(transfer); + fileReceiver.StopTransfer(transfer); + } + + pos.X += (downloadBarSize.X + 10); + } + } + + if (!gameStarted || Screen.Selected != GameMain.GameScreen) return; inGameHUD.DrawManually(spriteBatch); @@ -2297,40 +2351,6 @@ namespace Barotrauma.Networking } } - if (fileReceiver != null && fileReceiver.ActiveTransfers.Count > 0) - { - Vector2 pos = new Vector2(GameMain.NetLobbyScreen.InfoFrame.Rect.X, GameMain.GraphicsHeight - 35); - - GUI.DrawRectangle(spriteBatch, new Rectangle( - (int)pos.X, - (int)pos.Y, - fileReceiver.ActiveTransfers.Count * 210 + 10, - 32), - Color.Black * 0.8f, true); - - for (int i = 0; i < fileReceiver.ActiveTransfers.Count; i++) - { - var transfer = fileReceiver.ActiveTransfers[i]; - - GUI.DrawString(spriteBatch, - pos, - ToolBox.LimitString(TextManager.Get("DownloadingFile").Replace("[filename]", transfer.FileName), GUI.SmallFont, 200), - Color.White, null, 0, GUI.SmallFont); - GUI.DrawProgressBar(spriteBatch, new Vector2(pos.X, -pos.Y - 15), new Vector2(135, 15), transfer.Progress, Color.Green); - GUI.DrawString(spriteBatch, pos + new Vector2(5, 15), - MathUtils.GetBytesReadable((long)transfer.Received) + " / " + MathUtils.GetBytesReadable((long)transfer.FileSize), - Color.White, null, 0, GUI.SmallFont); - - if (GUI.DrawButton(spriteBatch, new Rectangle((int)pos.X + 140, (int)pos.Y + 18, 60, 15), TextManager.Get("Cancel"), new Color(0.47f, 0.13f, 0.15f, 0.08f))) - { - CancelFileTransfer(transfer); - fileReceiver.StopTransfer(transfer); - } - - pos.X += 210; - } - } - if (!ShowNetStats) return; netStats.Draw(spriteBatch, new Rectangle(300, 10, 300, 150)); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs b/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs index a33a49a0e..034fd96bc 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/ServerInfo.cs @@ -59,17 +59,32 @@ namespace Barotrauma.Networking return contentPackageHashes.SetEquals(myContentPackageHashes); } - public void CreatePreviewWindow(GUIMessageBox messageBox) + public void CreatePreviewWindow(GUIListBox listBox) { - var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), messageBox.Content.RectTransform), ServerName, textAlignment: Alignment.Center, font: GUI.LargeFont, wrap: true); + listBox.ClearChildren(); - var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), messageBox.Content.RectTransform)); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverMsg.Content.RectTransform), ServerMessage, wrap: true); + if (listBox == null) return; - var columnContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), messageBox.Content.RectTransform), isHorizontal: true) + var previewContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), listBox.Content.RectTransform, Anchor.Center)) + { + Stretch = true + }; + + var titleHolder = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.07f), previewContainer.RectTransform)) + { + IsHorizontal = true, + Stretch = true + }; + + var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), titleHolder.RectTransform), ServerName, font: GUI.LargeFont, wrap: true); + + new GUITextBlock(new RectTransform(Vector2.One, title.RectTransform), + TextManager.Get("ServerListVersion") + ": " + (string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion), textAlignment: Alignment.Right); + + var columnContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), previewContainer.RectTransform), isHorizontal: true) { Stretch = true, - RelativeSpacing = 0.05f + RelativeSpacing = 0.005f }; var columnLeft = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), columnContainer.RectTransform)) @@ -88,12 +103,72 @@ namespace Barotrauma.Networking // left column ----------------------------------------------------------------------------- //new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnLeft.RectTransform), IP + ":" + Port); - new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnLeft.RectTransform), - TextManager.Get("ServerListVersion") + ": " + (string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion)); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnLeft.RectTransform), + + var serverMsg = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), columnLeft.RectTransform)) { ScrollBarVisible = true }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), serverMsg.Content.RectTransform), ServerMessage, wrap: true) { CanBeFocused = false }; + + // right column ----------------------------------------------------------------------------- + + /*var playerCount = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListPlayers")); + new GUITextBlock(new RectTransform(Vector2.One, playerCount.RectTransform), PlayerCount + "/" + MaxPlayers, textAlignment: Alignment.Right); + + + new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), "Round running") + { + Selected = GameStarted, + CanBeFocused = false + };*/ + + var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("GameMode")); + new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform), TextManager.Get(string.IsNullOrEmpty(GameMode) ? "Unknown" : GameMode), textAlignment: Alignment.Right); + + var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("Traitors")); + new GUITextBlock(new RectTransform(Vector2.One, traitors.RectTransform), TextManager.Get(!TraitorsEnabled.HasValue ? "Unknown" : TraitorsEnabled.Value.ToString()), textAlignment: Alignment.Right); + + + var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListSubSelection")); + new GUITextBlock(new RectTransform(Vector2.One, subSelection.RectTransform), TextManager.Get(!SubSelectionMode.HasValue ? "Unknown" : SubSelectionMode.Value.ToString()), textAlignment: Alignment.Right); + + var modeSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListModeSelection")); + new GUITextBlock(new RectTransform(Vector2.One, modeSelection.RectTransform), TextManager.Get(!ModeSelectionMode.HasValue ? "Unknown" : ModeSelectionMode.Value.ToString()), textAlignment: Alignment.Right); + + var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListAllowSpectating")) + { + CanBeFocused = false + }; + if (!AllowSpectating.HasValue) + new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), allowSpectating.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); + else + allowSpectating.Selected = AllowSpectating.Value; + + var allowRespawn = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerSettingsAllowRespawning")) + { + CanBeFocused = false + }; + if (!AllowRespawn.HasValue) + new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), allowRespawn.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); + else + allowRespawn.Selected = AllowRespawn.Value; + + /*new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListHasPassword")) + { + Selected = HasPassword, + CanBeFocused = false + };*/ + + var usingWhiteList = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListUsingWhitelist")) + { + CanBeFocused = false + }; + if (!UsingWhiteList.HasValue) + new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), usingWhiteList.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); + else + usingWhiteList.Selected = UsingWhiteList.Value; + + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), columnRight.RectTransform), TextManager.Get("ServerListContentPackages")); - var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), columnLeft.RectTransform)); + var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), columnRight.RectTransform)); if (ContentPackageNames.Count == 0) { new GUITextBlock(new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get("Unknown"), textAlignment: Alignment.Center) @@ -143,7 +218,7 @@ namespace Barotrauma.Networking } } } - if (availableWorkshopUrls.Count > 0 ) + if (availableWorkshopUrls.Count > 0) { var workshopBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.15f), columnLeft.RectTransform), TextManager.Get("ServerListSubscribeMissingPackages")) { @@ -159,64 +234,6 @@ namespace Barotrauma.Networking workshopBtn.TextBlock.AutoScale = true; } } - - // right column ----------------------------------------------------------------------------- - - var playerCount = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListPlayers")); - new GUITextBlock(new RectTransform(Vector2.One, playerCount.RectTransform), PlayerCount + "/" + MaxPlayers, textAlignment: Alignment.Right); - - - new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), "Round running") - { - Selected = GameStarted, - CanBeFocused = false - }; - - var gameMode = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("GameMode")); - new GUITextBlock(new RectTransform(Vector2.One, gameMode.RectTransform), string.IsNullOrEmpty(GameMode) ? "Unknown" : GameMode, textAlignment: Alignment.Right); - - var traitors = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("Traitors")); - new GUITextBlock(new RectTransform(Vector2.One, traitors.RectTransform), !TraitorsEnabled.HasValue ? "Unknown" : TraitorsEnabled.Value.ToString(), textAlignment: Alignment.Right); - - - var subSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListSubSelection")); - new GUITextBlock(new RectTransform(Vector2.One, subSelection.RectTransform), !SubSelectionMode.HasValue ? "Unknown" : SubSelectionMode.Value.ToString(), textAlignment: Alignment.Right); - - var modeSelection = new GUITextBlock(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListModeSelection")); - new GUITextBlock(new RectTransform(Vector2.One, modeSelection.RectTransform), (!ModeSelectionMode.HasValue ? "Unknown" : ModeSelectionMode.Value.ToString()), textAlignment: Alignment.Right); - - var allowSpectating = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListAllowSpectating")) - { - CanBeFocused = false - }; - if (!AllowSpectating.HasValue) - new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), allowSpectating.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); - else - allowSpectating.Selected = AllowSpectating.Value; - - var allowRespawn = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), "Allow respawn") - { - CanBeFocused = false - }; - if (!AllowRespawn.HasValue) - new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), allowRespawn.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); - else - allowRespawn.Selected = AllowRespawn.Value; - - new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListHasPassword")) - { - Selected = HasPassword, - CanBeFocused = false - }; - - var usingWhiteList = new GUITickBox(new RectTransform(new Vector2(1, elementHeight), columnRight.RectTransform), TextManager.Get("ServerListUsingWhitelist")) - { - CanBeFocused = false - }; - if (!UsingWhiteList.HasValue) - new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.8f), usingWhiteList.Box.RectTransform, Anchor.Center), "?", textAlignment: Alignment.Center); - else - usingWhiteList.Selected = UsingWhiteList.Value; // ----------------------------------------------------------------------------- diff --git a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs index 3d76093fb..bcc5d714e 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs @@ -107,6 +107,56 @@ namespace Barotrauma.Steam return true; } + public static bool GetFavouriteServers(Action onServerFound, Action onServerRulesReceived, Action onFinished) + { + if (instance == null || !instance.isInitialized) + { + return false; + } + + var filter = new ServerList.Filter + { + { "appid", AppID.ToString() }, + { "gamedir", "Barotrauma" }, + { "secure", "1" } + }; + + //include unresponsive servers in the server list + + //the response is queried using the server's query port, not the game port, + //so it may be possible to play on the server even if it doesn't respond to server list queries + var query = instance.client.ServerList.Favourites(filter); + query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); }; + query.OnFinished = onFinished; + + return true; + } + + public static bool GetServersFromHistory(Action onServerFound, Action onServerRulesReceived, Action onFinished) + { + if (instance == null || !instance.isInitialized) + { + return false; + } + + var filter = new ServerList.Filter + { + { "appid", AppID.ToString() }, + { "gamedir", "Barotrauma" }, + { "secure", "1" } + }; + + //include unresponsive servers in the server list + + //the response is queried using the server's query port, not the game port, + //so it may be possible to play on the server even if it doesn't respond to server list queries + var query = instance.client.ServerList.History(filter); + query.OnUpdate += () => { UpdateServerQuery(query, onServerFound, onServerRulesReceived, includeUnresponsive: true); }; + query.OnFinished = onFinished; + + return true; + } + private static void UpdateServerQuery(ServerList.Request query, Action onServerFound, Action onServerRulesReceived, bool includeUnresponsive) { IEnumerable servers = includeUnresponsive ? @@ -548,6 +598,8 @@ namespace Barotrauma.Steam if (!allowFileOverwrite) { + // TODO: If you create a new mod via the workshop interface and enable it, it will show the error msg, but still allows you to enable the content. + if (File.Exists(newContentPackagePath)) { errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable") diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index 3c322d12d..655c6fb9d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -9,12 +9,9 @@ namespace Barotrauma { class CampaignUI { - public enum Tab { Map, Crew, Store } + public enum Tab { Map, Crew, Store, Repair } private Tab selectedTab; private GUIFrame[] tabs; - - private GUIButton startButton; - private GUIFrame topPanel; private GUIListBox characterList; @@ -26,6 +23,8 @@ namespace Barotrauma private GUIComponent selectedLocationInfo; private GUIListBox selectedMissionInfo; + private GUIButton repairHullsButton, repairItemsButton; + private GUIFrame characterPreviewFrame; private List tabButtons = new List(); @@ -39,10 +38,7 @@ namespace Barotrauma public GUIComponent MapContainer { get; private set; } - public GUIButton StartButton - { - get { return startButton; } - } + public GUIButton StartButton { get; private set; } public CampaignMode Campaign { get; } @@ -112,7 +108,7 @@ namespace Barotrauma tabs[(int)Tab.Crew] = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.7f), container.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(0.0f, topPanel.RectTransform.RelativeSize.Y) - }, color: Color.Black * 0.7f); + }, color: Color.Black * 0.9f); new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), tabs[(int)Tab.Crew].RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f) { CanBeFocused = false @@ -157,7 +153,7 @@ namespace Barotrauma tabs[(int)Tab.Store] = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.7f), container.RectTransform, Anchor.TopLeft) { RelativeOffset = new Vector2(0.1f, topPanel.RectTransform.RelativeSize.Y) - }, color: Color.Black * 0.7f); + }, color: Color.Black * 0.9f); new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), tabs[(int)Tab.Store].RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f) { CanBeFocused = false @@ -218,6 +214,97 @@ namespace Barotrauma } SelectItemCategory(MapEntityCategory.Equipment); + // repair tab ------------------------------------------------------------------------- + + tabs[(int)Tab.Repair] = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.5f), container.RectTransform, Anchor.TopLeft) + { + RelativeOffset = new Vector2(0.02f, topPanel.RectTransform.RelativeSize.Y) + }, color: Color.Black * 0.9f); + new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), tabs[(int)Tab.Repair].RectTransform, Anchor.Center), style: "OuterGlow", color: Color.Black * 0.7f) + { + CanBeFocused = false + }; + + var repairContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), tabs[(int)Tab.Repair].RectTransform, Anchor.Center)) + { + RelativeSpacing = 0.05f, + Stretch = true + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), crewContent.RectTransform), "", font: GUI.LargeFont) + { + TextGetter = GetMoney + }; + + var repairHullsHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), repairContent.RectTransform), childAnchor: Anchor.TopRight) + { + RelativeSpacing = 0.05f, + Stretch = true + }; + new GUIImage(new RectTransform(new Vector2(0.3f, 1.0f), repairHullsHolder.RectTransform, Anchor.CenterLeft), "RepairHullButton") + { + IgnoreLayoutGroups = true, + CanBeFocused = false + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairHullsHolder.RectTransform), TextManager.Get("RepairAllWalls"), textAlignment: Alignment.Right, font: GUI.LargeFont) + { + ForceUpperCase = true + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairHullsHolder.RectTransform), "500", textAlignment: Alignment.Right, font: GUI.LargeFont); + repairHullsButton = new GUIButton(new RectTransform(new Vector2(0.4f, 0.3f), repairHullsHolder.RectTransform), TextManager.Get("Repair"), style: "GUIButtonLarge") + { + OnClicked = (btn, userdata) => + { + if (campaign.Money >= CampaignMode.HullRepairCost) + { + campaign.Money -= CampaignMode.HullRepairCost; + campaign.PurchasedHullRepairs = true; + GameMain.Client?.SendCampaignState(); + btn.GetChild().Selected = true; + } + btn.Enabled = false; + return true; + } + }; + new GUITickBox(new RectTransform(new Vector2(0.65f), repairHullsButton.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(10, 0) }, "") + { + CanBeFocused = false + }; + + var repairItemsHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), repairContent.RectTransform), childAnchor: Anchor.TopRight) + { + RelativeSpacing = 0.05f, + Stretch = true + }; + new GUIImage(new RectTransform(new Vector2(0.3f, 1.0f), repairItemsHolder.RectTransform, Anchor.CenterLeft), "RepairItemsButton") + { + IgnoreLayoutGroups = true, + CanBeFocused = false + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairItemsHolder.RectTransform), TextManager.Get("RepairAllItems"), textAlignment: Alignment.Right, font: GUI.LargeFont) + { + ForceUpperCase = true + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), repairItemsHolder.RectTransform), "500", textAlignment: Alignment.Right, font: GUI.LargeFont); + repairItemsButton = new GUIButton(new RectTransform(new Vector2(0.4f, 0.3f), repairItemsHolder.RectTransform), TextManager.Get("Repair"), style: "GUIButtonLarge") + { + OnClicked = (btn, userdata) => + { + if (campaign.Money >= CampaignMode.ItemRepairCost) + { + campaign.Money -= CampaignMode.ItemRepairCost; + campaign.PurchasedItemRepairs = true; + GameMain.Client?.SendCampaignState(); + btn.GetChild().Selected = true; + } + btn.Enabled = false; + return true; + } + }; + new GUITickBox(new RectTransform(new Vector2(0.65f), repairItemsButton.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(10, 0) }, "") + { + CanBeFocused = false + }; + // mission info ------------------------------------------------------------------------- missionPanel = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.5f), container.RectTransform, Anchor.TopRight) @@ -330,8 +417,7 @@ namespace Barotrauma bool purchaseableItemsFound = false; foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List) { - var itemPrefab = mapEntityPrefab as ItemPrefab; - if (itemPrefab == null) { continue; } + if (!(mapEntityPrefab is ItemPrefab itemPrefab)) { continue; } PriceInfo priceInfo = itemPrefab.GetPrice(Campaign.Map.CurrentLocation); if (priceInfo != null) { purchaseableItemsFound = true; break; } @@ -349,8 +435,7 @@ namespace Barotrauma { //refresh store view SelectItemCategory(MapEntityCategory.Equipment); - } - + } } private void DrawMap(SpriteBatch spriteBatch, GUICustomComponent mapContainer) @@ -452,7 +537,7 @@ namespace Barotrauma RefreshMissionTab(selectedMission); - startButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.7f), missionContent.RectTransform, Anchor.CenterRight), + StartButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.7f), missionContent.RectTransform, Anchor.CenterRight), TextManager.Get("StartCampaignButton"), style: "GUIButtonLarge") { IgnoreLayoutGroups = true, @@ -461,7 +546,7 @@ namespace Barotrauma }; if (GameMain.Client != null) { - startButton.Visible = !GameMain.Client.GameStarted && + StartButton.Visible = !GameMain.Client.GameStarted && (GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)); } @@ -508,10 +593,10 @@ namespace Barotrauma CanBeFocused = false }; - if (startButton != null) + if (StartButton != null) { - startButton.Enabled = true; - startButton.Visible = GameMain.Client == null || + StartButton.Enabled = true; + StartButton.Visible = GameMain.Client == null || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageRound) || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign); } @@ -597,8 +682,7 @@ namespace Barotrauma private bool BuyItem(GUIComponent component, object obj) { - PurchasedItem pi = obj as PurchasedItem; - if (pi == null || pi.ItemPrefab == null) return false; + if (!(obj is PurchasedItem pi) || pi.ItemPrefab == null) return false; if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)) { @@ -616,8 +700,7 @@ namespace Barotrauma private bool SellItem(GUIComponent component, object obj) { - PurchasedItem pi = obj as PurchasedItem; - if (pi == null || pi.ItemPrefab == null) return false; + if (!(obj is PurchasedItem pi) || pi.ItemPrefab == null) return false; if (GameMain.Client != null && !GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)) { @@ -659,6 +742,20 @@ namespace Barotrauma { button.Selected = (Tab)button.UserData == tab; } + + switch (selectedTab) + { + case Tab.Repair: + repairHullsButton.Enabled = + !Campaign.PurchasedHullRepairs && Campaign.Money >= CampaignMode.HullRepairCost && + (GameMain.Client == null || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)); + repairHullsButton.GetChild().Selected = Campaign.PurchasedHullRepairs; + repairItemsButton.Enabled = + !Campaign.PurchasedItemRepairs && Campaign.Money >= CampaignMode.ItemRepairCost && + (GameMain.Client == null || GameMain.Client.HasPermission(Networking.ClientPermissions.ManageCampaign)); + repairItemsButton.GetChild().Selected = Campaign.PurchasedItemRepairs; + break; + } } private bool SelectItemCategory(MapEntityCategory category) @@ -668,8 +765,7 @@ namespace Barotrauma int width = storeItemList.Rect.Width; foreach (MapEntityPrefab mapEntityPrefab in MapEntityPrefab.List) { - var itemPrefab = mapEntityPrefab as ItemPrefab; - if (itemPrefab == null || !itemPrefab.Category.HasFlag(category)) continue; + if (!(mapEntityPrefab is ItemPrefab itemPrefab) || !itemPrefab.Category.HasFlag(category)) continue; PriceInfo priceInfo = itemPrefab.GetPrice(Campaign.Map.CurrentLocation); if (priceInfo == null) continue; @@ -707,9 +803,8 @@ namespace Barotrauma } if (prevInfoFrame != null) { tabs[(int)selectedTab].RemoveChild(prevInfoFrame); } - - CharacterInfo characterInfo = selection as CharacterInfo; - if (characterInfo == null) { return false; } + + if (!(selection is CharacterInfo characterInfo)) { return false; } if (Character.Controlled != null && characterInfo == Character.Controlled.Info) { return false; } if (characterPreviewFrame == null || characterPreviewFrame.UserData != characterInfo) @@ -761,11 +856,9 @@ namespace Barotrauma private bool HireCharacter(GUIButton button, object selection) { - CharacterInfo characterInfo = selection as CharacterInfo; - if (characterInfo == null) { return false; } + if (!(selection is CharacterInfo characterInfo)) { return false; } - SinglePlayerCampaign spCampaign = Campaign as SinglePlayerCampaign; - if (spCampaign == null) + if (!(Campaign is SinglePlayerCampaign spCampaign)) { DebugConsole.ThrowError("Characters can only be hired in the single player campaign.\n" + Environment.StackTrace); return false; @@ -784,11 +877,9 @@ namespace Barotrauma private bool FireCharacter(GUIButton button, object selection) { - CharacterInfo characterInfo = selection as CharacterInfo; - if (characterInfo == null) return false; + if (!(selection is CharacterInfo characterInfo)) return false; - SinglePlayerCampaign spCampaign = Campaign as SinglePlayerCampaign; - if (spCampaign == null) + if (!(Campaign is SinglePlayerCampaign spCampaign)) { DebugConsole.ThrowError("Characters can only be fired in the single player campaign.\n" + Environment.StackTrace); return false; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index 8dfbbdd0b..bb865ab01 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -42,16 +42,16 @@ namespace Barotrauma private bool showParamsEditor; private bool showSpritesheet; private bool isFreezed; - private bool autoFreeze = true; - private bool limbPairEditing = true; - private bool uniformScaling = true; - private bool lockSpriteOrigin = true; + private bool autoFreeze; + private bool limbPairEditing; + private bool uniformScaling; + private bool lockSpriteOrigin; private bool lockSpritePosition; private bool lockSpriteSize; private bool recalculateCollider; private bool copyJointSettings; private bool displayColliders; - private bool displayWearables = true; + private bool displayWearables; private bool displayBackgroundColor; private bool ragdollResetRequiresForceLoading; private bool animationResetRequiresForceLoading; @@ -89,10 +89,17 @@ namespace Barotrauma public override void Select() { base.Select(); + + SoundPlayer.OverrideMusicType = "none"; + SoundPlayer.OverrideMusicDuration = null; + GameMain.SoundManager.SetCategoryGainMultiplier("default", 0.0f); + GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", 0.0f); + GUI.ForceMouseOn(null); CalculateSpritesheetPosition(); if (Submarine.MainSub == null) { + ResetVariables(); Submarine.MainSub = new Submarine("Content/AnimEditor.sub"); Submarine.MainSub.Load(unloadPrevious: false, showWarningMessages: false); originalWall = new WallGroup(new List(Structure.WallList)); @@ -101,6 +108,10 @@ namespace Barotrauma isEndlessRunner = true; GameMain.LightManager.LightingEnabled = false; } + else if (instance == null) + { + ResetVariables(); + } Submarine.MainSub.GodMode = true; if (Character.Controlled == null) { @@ -118,31 +129,65 @@ namespace Barotrauma instance = this; } + private void ResetVariables() + { + editAnimations = false; + editLimbs = false; + editJoints = false; + editIK = false; + showRagdoll = false; + showParamsEditor = false; + showSpritesheet = false; + isFreezed = false; + autoFreeze = true; + limbPairEditing = true; + uniformScaling = true; + lockSpriteOrigin = false; + lockSpritePosition = false; + lockSpriteSize = false; + recalculateCollider = false; + copyJointSettings = false; + displayColliders = false; + displayWearables = true; + displayBackgroundColor = false; + ragdollResetRequiresForceLoading = false; + animationResetRequiresForceLoading = false; + isExtrudingJoint = false; + isDrawingJoint = false; + Wizard.instance = null; + } + private void Reset() { - AnimParams.ForEach(a => a.Reset(true)); - RagdollParams.Reset(true); - RagdollParams.ClearHistory(); - CurrentAnimation.ClearHistory(); - if (!character.Removed) + ResetVariables(); + if (character != null) { - character.Remove(); + AnimParams.ForEach(a => a.Reset(true)); + RagdollParams.Reset(true); + RagdollParams.ClearHistory(); + CurrentAnimation.ClearHistory(); + if (!character.Removed) + { + character.Remove(); + } + character = null; } - character = null; } public override void Deselect() { base.Deselect(); + + SoundPlayer.OverrideMusicType = null; + GameMain.SoundManager.SetCategoryGainMultiplier("default", GameMain.Config.SoundVolume); + GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", GameMain.Config.SoundVolume); + GUI.ForceMouseOn(null); if (isEndlessRunner) { Submarine.MainSub.Remove(); isEndlessRunner = false; - if (character != null) - { - Reset(); - } + Reset(); GameMain.World.ProcessChanges(); } else @@ -175,7 +220,7 @@ namespace Barotrauma { //base.AddToGUIUpdateList(); rightPanel.AddToGUIUpdateList(); - Wizard.Instance.AddToGUIUpdateList(); + Wizard.instance?.AddToGUIUpdateList(); if (displayBackgroundColor) { backgroundColorPanel.AddToGUIUpdateList(); @@ -207,7 +252,7 @@ namespace Barotrauma base.Update(deltaTime); spriteSheetRect = CalculateSpritesheetRectangle(); // Handle shortcut keys - if (GUI.KeyboardDispatcher.Subscriber == null) + if (GUI.KeyboardDispatcher.Subscriber == null && Wizard.instance == null) { if (PlayerInput.KeyDown(Keys.LeftControl)) { @@ -396,7 +441,7 @@ namespace Barotrauma } } } - if (!isFreezed) + if (!isFreezed && Wizard.instance == null) { if (character.AnimController.Invalid) { @@ -1131,7 +1176,22 @@ namespace Barotrauma character = Character.Create(configFile, spawnPosition, ToolBox.RandomSeed(8), hasAi: false, ragdoll: ragdoll); selectedJob = null; } - character.dontFollowCursor = dontFollowCursor; + if (character != null) + { + character.dontFollowCursor = dontFollowCursor; + } + if (character == null) + { + if (currentCharacterConfig == configFile) + { + return null; + } + else + { + // Respawn the current character; + SpawnCharacter(currentCharacterConfig); + } + } OnPostSpawn(); return character; } @@ -1247,27 +1307,33 @@ namespace Barotrauma string speciesName = name; // Config file string configFilePath = Path.Combine(mainFolder, $"{speciesName}.xml").Replace(@"\", @"/"); - if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).None(path => path.Contains(speciesName))) + if (ContentPackage.GetFilesOfType(GameMain.SelectedPackages, ContentType.Character).Any(path => path.Contains(speciesName))) { - // Create the config file - XElement mainElement = new XElement("Character", - new XAttribute("name", speciesName), - new XAttribute("humanoid", isHumanoid), - new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))), - new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))), - new XElement("health"), - new XElement("ai")); - XDocument doc = new XDocument(mainElement); - if (!Directory.Exists(mainFolder)) - { - Directory.CreateDirectory(mainFolder); - } - doc.Save(configFilePath); - // Add to the selected content package - contentPackage.AddFile(configFilePath, ContentType.Character); - contentPackage.Save(contentPackage.Path); - DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); + GUI.AddMessage(GetCharacterEditorTranslation("ExistingCharacterFound"), Color.Red, font: GUI.LargeFont); + // TODO: add a prompt: "Do you want to replace it?" + functionality + return false; } + + // Create the config file + XElement mainElement = new XElement("Character", + new XAttribute("name", speciesName), + new XAttribute("humanoid", isHumanoid), + new XElement("ragdolls", new XAttribute("folder", Path.Combine(mainFolder, $"Ragdolls/").Replace(@"\", @"/"))), + new XElement("animations", new XAttribute("folder", Path.Combine(mainFolder, $"Animations/").Replace(@"\", @"/"))), + new XElement("health"), + new XElement("ai")); + + XDocument doc = new XDocument(mainElement); + if (!Directory.Exists(mainFolder)) + { + Directory.CreateDirectory(mainFolder); + } + doc.Save(configFilePath); + // Add to the selected content package + contentPackage.AddFile(configFilePath, ContentType.Character); + contentPackage.Save(contentPackage.Path); + DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); + // Ragdoll string ragdollFolder = RagdollParams.GetFolder(speciesName); string ragdollPath = RagdollParams.GetDefaultFile(speciesName); @@ -1278,12 +1344,20 @@ namespace Barotrauma string animFolder = AnimationParams.GetFolder(speciesName); foreach (AnimationType animType in Enum.GetValues(typeof(AnimationType))) { - if (animType != AnimationType.NotDefined) + switch (animType) { - Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid); - string fullPath = AnimationParams.GetDefaultFile(speciesName, animType); - AnimationParams.Create(fullPath, speciesName, animType, type); + case AnimationType.Walk: + case AnimationType.Run: + if (!ragdollParams.CanEnterSubmarine) { continue; } + break; + case AnimationType.SwimSlow: + case AnimationType.SwimFast: + break; + default: continue; } + Type type = AnimationParams.GetParamTypeFromAnimType(animType, isHumanoid); + string fullPath = AnimationParams.GetDefaultFile(speciesName, animType); + AnimationParams.Create(fullPath, speciesName, animType, type); } if (!AllFiles.Contains(configFilePath)) { @@ -3764,7 +3838,7 @@ namespace Barotrauma void RecalculateCollider(Limb l) { // We want the collider to be slightly smaller than the source rect, because the source rect is usually a bit bigger than the graphic. - float multiplier = 0.75f; + float multiplier = 0.85f; l.body.SetSize(new Vector2(ConvertUnits.ToSimUnits(width), ConvertUnits.ToSimUnits(height)) * RagdollParams.LimbScale * RagdollParams.TextureScale * multiplier); TryUpdateLimbParam(l, "radius", ConvertUnits.ToDisplayUnits(l.body.radius / RagdollParams.LimbScale / RagdollParams.TextureScale)); TryUpdateLimbParam(l, "width", ConvertUnits.ToDisplayUnits(l.body.width / RagdollParams.LimbScale / RagdollParams.TextureScale)); @@ -4283,7 +4357,7 @@ namespace Barotrauma private List jointXElements = new List(); private List jointGUIElements = new List(); - private static Wizard instance; + public static Wizard instance; public static Wizard Instance { get @@ -4314,7 +4388,6 @@ namespace Barotrauma break; case Tab.None: default: - //activeView = null; instance = null; break; } @@ -4343,7 +4416,7 @@ namespace Barotrauma GUITextBox xmlPathElement = null; void UpdatePaths() { - string pathBase = $"Content/Characters/{Name}/{Name}"; + string pathBase = $"Mods/Characters/{Name}/{Name}"; XMLPath = $"{pathBase}.xml"; TexturePath = $"{pathBase}.png"; texturePathElement.Text = TexturePath; @@ -4422,7 +4495,7 @@ namespace Barotrauma // Cancel box.Buttons[0].OnClicked += (b, d) => { - Instance.SelectTab(Tab.None); + Wizard.Instance.SelectTab(Tab.None); return true; }; // Next @@ -4434,7 +4507,7 @@ namespace Barotrauma texturePathElement.Flash(Color.Red); return false; } - Instance.SelectTab(Tab.Ragdoll); + Wizard.Instance.SelectTab(Tab.Ragdoll); return true; }; return box; @@ -4575,7 +4648,7 @@ namespace Barotrauma // Previous box.Buttons[0].OnClicked += (b, d) => { - Instance.SelectTab(Tab.Character); + Wizard.Instance.SelectTab(Tab.Character); return true; }; // Parse and create @@ -4666,7 +4739,7 @@ namespace Barotrauma { GUI.AddMessage(GetCharacterEditorTranslation("CharacterCreated").Replace("[name]", Name), Color.Green, font: GUI.Font); } - Instance.SelectTab(Tab.None); + Wizard.Instance.SelectTab(Tab.None); return true; }; return box; @@ -4846,23 +4919,27 @@ namespace Barotrauma int width = rectInputs[2].IntValue; int height = rectInputs[3].IntValue; var colliderAttributes = new List(); - if (width == height) - { - colliderAttributes.Add(new XAttribute("radius", width / 2)); - } - else - { - if (height > width) - { - colliderAttributes.Add(new XAttribute("radius", width / 2)); - colliderAttributes.Add(new XAttribute("height", height - width)); - } - else - { - colliderAttributes.Add(new XAttribute("radius", height / 2)); - colliderAttributes.Add(new XAttribute("width", width - height)); - } - } + // Capsules/Circles + //if (width == height) + //{ + // colliderAttributes.Add(new XAttribute("radius", (int)(width / 2 * 0.85f))); + //} + //else + //{ + // if (height > width) + // { + // colliderAttributes.Add(new XAttribute("radius", (int)(width / 2 * 0.85f))); + // colliderAttributes.Add(new XAttribute("height",(int) (height - width * 0.85f))); + // } + // else + // { + // colliderAttributes.Add(new XAttribute("radius", (int)(height / 2 * 0.85f))); + // colliderAttributes.Add(new XAttribute("width", (int)(width - height * 0.85f))); + // } + //} + // Rectangles + colliderAttributes.Add(new XAttribute("height", (int)(height * 0.85f))); + colliderAttributes.Add(new XAttribute("width", (int)(width * 0.85f))); idToCodeName.TryGetValue(id, out string notes); LimbXElements.Add(id.ToString(), new XElement("limb", new XAttribute("id", id), diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index a1c7438b6..3a7b00d0b 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -261,7 +261,7 @@ namespace Barotrauma public NetLobbyScreen() { - defaultModeContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), Frame.RectTransform, Anchor.Center), style: null); + defaultModeContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), Frame.RectTransform, Anchor.Center) { MaxSize = new Point(int.MaxValue, GameMain.GraphicsHeight - 100) }, style: null); campaignContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.75f), Frame.RectTransform, Anchor.TopCenter), style: null) { Visible = false @@ -737,11 +737,15 @@ namespace Barotrauma spectateButton.Visible = GameMain.Client.GameStarted; ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Selected = false; - if (campaignUI?.StartButton != null) + if (campaignUI != null) { - campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && - (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || - GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); + //SelectTab(Tab.Map); + if (campaignUI.StartButton != null) + { + campaignUI.StartButton.Visible = !GameMain.Client.GameStarted && + (GameMain.Client.HasPermission(ClientPermissions.ManageRound) || + GameMain.Client.HasPermission(ClientPermissions.ManageCampaign)); + } } GameMain.Client.SetReadyToStart(ReadyToStartBox); } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs index 672ffcef6..196956df2 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs @@ -57,32 +57,20 @@ namespace Barotrauma var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 1.0f), paddedFrame.RectTransform, Anchor.CenterLeft)) { Stretch = true, RelativeSpacing = 0.5f }; - menu = new GUIFrame(new RectTransform(new Point(width, height), GUI.Canvas, Anchor.Center)); + var infoHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform)) { RelativeSpacing = 0.05f }; - new GUITextBlock(new RectTransform(new Vector2(0.95f, 0.133f), menu.RectTransform, Anchor.TopCenter), - TextManager.Get("JoinServer"), textAlignment: Alignment.Left, font: GUI.LargeFont) { ForceUpperCase = true }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), infoHolder.RectTransform, Anchor.Center), TextManager.Get("JoinServer"), font: GUI.LargeFont) + { ForceUpperCase = true }; - var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), menu.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.03f) }, style: null); - - //------------------------------------------------------------------------------------- - //left column - //------------------------------------------------------------------------------------- - - var leftColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.25f, 0.92f), paddedFrame.RectTransform, Anchor.TopLeft)); - - //spacing - new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), leftColumn.RectTransform), style: null); - - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("YourName")); - clientNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.045f), leftColumn.RectTransform), "") + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoHolder.RectTransform), TextManager.Get("YourName")); + clientNameBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), infoHolder.RectTransform), "") { Text = GameMain.Config.DefaultPlayerName }; clientNameBox.OnTextChanged += RefreshJoinButtonState; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("ServerIP")); - // TODO: Show IP on server info window - ipBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.045f), leftColumn.RectTransform), ""); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoHolder.RectTransform), TextManager.Get("ServerIP")); + ipBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), infoHolder.RectTransform), ""); ipBox.OnTextChanged += RefreshJoinButtonState; ipBox.OnSelected += (sender, key) => { @@ -92,31 +80,81 @@ namespace Barotrauma sender.UserData = null; } }; - - //spacing - new GUIFrame(new RectTransform(new Vector2(1.0f, 0.45f), leftColumn.RectTransform), style: null); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("FilterServers")); - searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), ""); + var filterHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), leftColumn.RectTransform)) { RelativeSpacing = 0.05f }; - //spacing - new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), leftColumn.RectTransform), style: null); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), filterHolder.RectTransform), TextManager.Get("FilterServers")); + searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.13f), filterHolder.RectTransform), ""); + + var tickBoxHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), filterHolder.RectTransform)); searchBox.OnTextChanged += (txtBox, txt) => { FilterServers(); return true; }; - filterPassword = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("FilterPassword")); + filterPassword = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterPassword")); filterPassword.OnSelected += (tickBox) => { FilterServers(); return true; }; - filterIncompatible = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("FilterIncompatibleServers")); + filterIncompatible = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterIncompatibleServers")); filterIncompatible.OnSelected += (tickBox) => { FilterServers(); return true; }; - filterFull = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("FilterFullServers")); + filterFull = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterFullServers")); filterFull.OnSelected += (tickBox) => { FilterServers(); return true; }; - filterEmpty = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), leftColumn.RectTransform), TextManager.Get("FilterEmptyServers")); + filterEmpty = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.27f), tickBoxHolder.RectTransform), TextManager.Get("FilterEmptyServers")); filterEmpty.OnSelected += (tickBox) => { FilterServers(); return true; }; //------------------------------------------------------------------------------------- //right column //------------------------------------------------------------------------------------- + var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - leftColumn.RectTransform.RelativeSize.X - 0.017f, 1.0f), + paddedFrame.RectTransform, Anchor.CenterRight)) + { + RelativeSpacing = 0.02f, + Stretch = true + }; + + var serverListHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform)) { Stretch = true, RelativeSpacing = 0.02f }; + + serverList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListHolder.RectTransform, Anchor.Center)) + { + OnSelected = (btn, obj) => { + ServerInfo serverInfo = (ServerInfo)obj; + + serverInfo.CreatePreviewWindow(serverPreview); + + return true; + } + }; + + serverList.OnSelected += SelectServer; + + serverPreview = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), serverListHolder.RectTransform, Anchor.Center)); + + columnRelativeWidth = new float[] { 0.04f, 0.02f, 0.044f, 0.77f, 0.02f, 0.075f, 0.06f }; + + var buttonContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.075f), rightColumn.RectTransform), style: null); + + GUIButton button = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform, Anchor.TopLeft), + TextManager.Get("Back"), style: "GUIButtonLarge") + { + OnClicked = GameMain.MainMenuScreen.ReturnToMainMenu + }; + + var refreshButton = new GUIButton(new RectTransform(new Vector2(buttonContainer.Rect.Height / (float)buttonContainer.Rect.Width, 0.9f), buttonContainer.RectTransform, Anchor.Center), + "", style: "GUIButtonRefresh") { + + ToolTip = TextManager.Get("ServerListRefresh"), + OnClicked = RefreshServers + }; + + joinButton = new GUIButton(new RectTransform(new Vector2(0.25f, 0.9f), buttonContainer.RectTransform, Anchor.TopRight), + TextManager.Get("ServerListJoin"), style: "GUIButtonLarge") + { + OnClicked = JoinServer, + Enabled = false + }; + + //------------------------------------------------------------------------------------- + //right column + //------------------------------------------------------------------------------------- + var rightColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - leftColumn.RectTransform.RelativeSize.X - 0.017f, 0.97f), paddedFrame.RectTransform, Anchor.TopRight)) { @@ -360,7 +398,7 @@ namespace Barotrauma private void AddToServerList(ServerInfo serverInfo) { - var serverFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), serverList.Content.RectTransform) { MinSize = new Point(0, 20) }, + var serverFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.06f), serverList.Content.RectTransform) { MinSize = new Point(0, 35) }, style: "InnerFrame", color: Color.White * 0.5f) { UserData = serverInfo @@ -398,19 +436,6 @@ namespace Barotrauma UserData = "password" }; - new GUIButton(new RectTransform(new Vector2(columnRelativeWidth[2], 0.8f), serverContent.RectTransform, Anchor.Center), style: "GUIButtonServerListInfo") { - ToolTip = TextManager.Get("ServerListInfo"), - OnClicked = (btn, obj) => { - SelectServer(null, serverInfo); - var msgBox = new GUIMessageBox("", "", new string[] { TextManager.Get("Cancel"), TextManager.Get("ServerListJoin") }, 550, 400); - msgBox.Buttons[0].OnClicked += msgBox.Close; - msgBox.Buttons[1].OnClicked += JoinServer; - msgBox.Buttons[1].OnClicked += msgBox.Close; - serverInfo.CreatePreviewWindow(msgBox); - return true; - } - }; - var serverName = new GUITextBlock(new RectTransform(new Vector2(columnRelativeWidth[3], 1.0f), serverContent.RectTransform), serverInfo.ServerName, style: "GUIServerListTextBox"); var gameStartedBox = new GUITickBox(new RectTransform(new Vector2(columnRelativeWidth[4], 0.4f), serverContent.RectTransform, Anchor.Center), diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs index 89cbf2ef2..a9918309b 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SteamWorkshopScreen.cs @@ -43,6 +43,7 @@ namespace Barotrauma private ContentPackage itemContentPackage; private Facepunch.Steamworks.Workshop.Editor itemEditor; + //private Facepunch.Steamworks.Overlay overlay; public SteamWorkshopScreen() { @@ -637,6 +638,12 @@ namespace Barotrauma OutlineColor = new Color(72, 124, 77, 255), OnClicked = (btn, userdata) => { + // Failed attempt, might have to be activated before accessing because as of now it just throws a null for overlay + /*if (overlay.Enabled) + { + overlay.OpenUrl("steam://url/CommunityFilePage/" + item.Id); + }*/ + System.Diagnostics.Process.Start("steam://url/CommunityFilePage/" + item.Id); return true; } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index 52ac33d4a..8d45d1ae0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -310,6 +310,12 @@ namespace Barotrauma RelativeSpacing = 0.01f, Stretch = true }; + + new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), tabButtonHolder.RectTransform), TextManager.Get("MapEntityCategory.All"), style: "GUITabButton") + { + OnClicked = (btn, userdata) => { ClearFilter(); return true; } + }; + foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory))) { entityCategoryButtons.Add(new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), tabButtonHolder.RectTransform), @@ -1011,7 +1017,7 @@ namespace Barotrauma var previewImageButtonHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), rightColumn.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.05f }; - new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageGenerate")) + new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), previewImageButtonHolder.RectTransform), TextManager.Get("SubPreviewImageCreate")) { OnClicked = (btn, userdata) => { diff --git a/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs b/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs index 0d756b322..cb0e9cd57 100644 --- a/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs +++ b/Barotrauma/BarotraumaClient/Source/Serialization/SerializableEntityEditor.cs @@ -277,13 +277,17 @@ namespace Barotrauma { component.RectTransform.Parent = layoutGroup.RectTransform; component.RectTransform.RepositionChildInHierarchy(childIndex); + Recalculate(); + } + public void Recalculate() + { int contentHeight = ContentHeight; RectTransform.NonScaledSize = new Point(RectTransform.NonScaledSize.X, contentHeight); layoutGroup.RectTransform.NonScaledSize = new Point(layoutGroup.RectTransform.NonScaledSize.X, contentHeight); } - private GUIComponent CreateNewField(SerializableProperty property, ISerializableEntity entity) + public GUIComponent CreateNewField(SerializableProperty property, ISerializableEntity entity) { object value = property.GetValue(entity); if (property.PropertyType == typeof(string) && value == null) @@ -351,7 +355,7 @@ namespace Barotrauma return propertyField; } - private GUIComponent CreateBoolField(ISerializableEntity entity, SerializableProperty property, bool value, string displayName, string toolTip) + public GUIComponent CreateBoolField(ISerializableEntity entity, SerializableProperty property, bool value, string displayName, string toolTip) { GUITickBox propertyTickBox = new GUITickBox(new RectTransform(new Point(Rect.Width, elementHeight), layoutGroup.RectTransform), displayName) { @@ -367,11 +371,11 @@ namespace Barotrauma return true; } }; - Fields.Add(property.Name, new GUIComponent[] { propertyTickBox }); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { propertyTickBox }); } return propertyTickBox; } - private GUIComponent CreateIntField(ISerializableEntity entity, SerializableProperty property, int value, string displayName, string toolTip) + public GUIComponent CreateIntField(ISerializableEntity entity, SerializableProperty property, int value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -395,11 +399,11 @@ namespace Barotrauma TrySendNetworkUpdate(entity, property); } }; - Fields.Add(property.Name, new GUIComponent[] { numberInput }); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { numberInput }); } return frame; } - private GUIComponent CreateFloatField(ISerializableEntity entity, SerializableProperty property, float value, string displayName, string toolTip) + public GUIComponent CreateFloatField(ISerializableEntity entity, SerializableProperty property, float value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -425,11 +429,11 @@ namespace Barotrauma TrySendNetworkUpdate(entity, property); } }; - Fields.Add(property.Name, new GUIComponent[] { numberInput }); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { numberInput }); } return frame; } - private GUIComponent CreateEnumField(ISerializableEntity entity, SerializableProperty property, object value, string displayName, string toolTip) + public GUIComponent CreateEnumField(ISerializableEntity entity, SerializableProperty property, object value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, elementHeight), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -454,11 +458,11 @@ namespace Barotrauma return true; }; enumDropDown.SelectItem(value); - Fields.Add(property.Name, new GUIComponent[] { enumDropDown }); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { enumDropDown }); } return frame; } - private GUIComponent CreateEnumFlagField(ISerializableEntity entity, SerializableProperty property, object value, string displayName, string toolTip) + public GUIComponent CreateEnumFlagField(ISerializableEntity entity, SerializableProperty property, object value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, elementHeight), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -487,18 +491,21 @@ namespace Barotrauma return true; }; - Fields.Add(property.Name, new GUIComponent[] { enumDropDown }); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { enumDropDown }); } return frame; } - private GUIComponent CreateStringField(ISerializableEntity entity, SerializableProperty property, string value, string displayName, string toolTip) + public GUIComponent CreateStringField(ISerializableEntity entity, SerializableProperty property, string value, string displayName, string toolTip) { - var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, elementHeight), layoutGroup.RectTransform), color: Color.Transparent); + var frame = new GUILayoutGroup(new RectTransform(new Point(Rect.Width, elementHeight), layoutGroup.RectTransform), isHorizontal: true) + { + Stretch = true + }; var label = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), frame.RectTransform), displayName, font: GUI.SmallFont, textAlignment: Alignment.Left) { ToolTip = toolTip }; - GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform, Anchor.TopRight)) + GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1), frame.RectTransform)) { ToolTip = toolTip, Font = GUI.SmallFont, @@ -514,11 +521,36 @@ namespace Barotrauma return true; } }; - Fields.Add(property.Name, new GUIComponent[] { propertyBox }); + string translationTextTag = property.GetAttribute()?.translationTextTag; + if (translationTextTag != null) + { + new GUIButton(new RectTransform(new Vector2(0.1f, 1), frame.RectTransform, Anchor.TopRight), "...") + { + OnClicked = (bt, userData) => { CreateTextPicker(translationTextTag, entity, property, propertyBox); return true; } + }; + propertyBox.OnTextChanged += (tb, text) => + { + string translatedText = TextManager.Get(text, returnNull: true); + if (translatedText == null) + { + propertyBox.TextColor = Color.Gray; + propertyBox.ToolTip = TextManager.Get("StringPropertyCannotTranslate").Replace("[tag]", text ?? ""); + } + else + { + propertyBox.TextColor = Color.LightGreen; + propertyBox.ToolTip = TextManager.Get("StringPropertyTranslate").Replace("[translation]", translatedText); + } + return true; + }; + propertyBox.Text = value; + } + + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, new GUIComponent[] { propertyBox }); } return frame; } - private GUIComponent CreatePointField(ISerializableEntity entity, SerializableProperty property, Point value, string displayName, string toolTip) + public GUIComponent CreatePointField(ISerializableEntity entity, SerializableProperty property, Point value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -566,11 +598,11 @@ namespace Barotrauma }; fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } - private GUIComponent CreateVector2Field(ISerializableEntity entity, SerializableProperty property, Vector2 value, string displayName, string toolTip) + public GUIComponent CreateVector2Field(ISerializableEntity entity, SerializableProperty property, Vector2 value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.4f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -620,11 +652,11 @@ namespace Barotrauma }; fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } - private GUIComponent CreateVector3Field(ISerializableEntity entity, SerializableProperty property, Vector3 value, string displayName, string toolTip) + public GUIComponent CreateVector3Field(ISerializableEntity entity, SerializableProperty property, Vector3 value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -678,11 +710,11 @@ namespace Barotrauma }; fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } - private GUIComponent CreateVector4Field(ISerializableEntity entity, SerializableProperty property, Vector4 value, string displayName, string toolTip) + public GUIComponent CreateVector4Field(ISerializableEntity entity, SerializableProperty property, Vector4 value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -740,14 +772,14 @@ namespace Barotrauma }; fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } - private GUIComponent CreateColorField(ISerializableEntity entity, SerializableProperty property, Color value, string displayName, string toolTip) + public GUIComponent CreateColorField(ISerializableEntity entity, SerializableProperty property, Color value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); - var label = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), frame.RectTransform) { MinSize = new Point(80, 26)}, displayName, font: GUI.SmallFont) + var label = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), frame.RectTransform) { MinSize = new Point(80, 26) }, displayName, font: GUI.SmallFont) { ToolTip = toolTip }; @@ -807,11 +839,11 @@ namespace Barotrauma colorBox.Color = (Color)property.GetValue(entity); fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } - private GUIComponent CreateRectangleField(ISerializableEntity entity, SerializableProperty property, Rectangle value, string displayName, string toolTip) + public GUIComponent CreateRectangleField(ISerializableEntity entity, SerializableProperty property, Rectangle value, string displayName, string toolTip) { var frame = new GUIFrame(new RectTransform(new Point(Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform), color: Color.Transparent); var label = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), frame.RectTransform), displayName, font: GUI.SmallFont) @@ -867,9 +899,43 @@ namespace Barotrauma }; fields[i] = numberInput; } - Fields.Add(property.Name, fields); + if (!Fields.ContainsKey(property.Name)) { Fields.Add(property.Name, fields); } return frame; } + + public void CreateTextPicker(string textTag, ISerializableEntity entity, SerializableProperty property, GUITextBox textBox) + { + var msgBox = new GUIMessageBox("", "", new string[] { TextManager.Get("Cancel") }, width: 300, height: 400); + msgBox.Buttons[0].OnClicked = msgBox.Close; + + var textList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), msgBox.Content.RectTransform, Anchor.TopCenter)) + { + OnSelected = (component, userData) => + { + string text = userData as string ?? ""; + + if (property.TrySetValue(entity, text)) + { + TrySendNetworkUpdate(entity, property); + textBox.Text = (string)property.GetValue(entity); + textBox.Deselect(); + } + return true; + } + }; + + textTag = textTag.ToLowerInvariant(); + var tagTextPairs = TextManager.GetAllTagTextPairs(); + foreach (KeyValuePair tagTextPair in tagTextPairs) + { + if (!tagTextPair.Key.StartsWith(textTag)) { continue; } + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) }, + ToolBox.LimitString(tagTextPair.Value, GUI.Font, textList.Content.Rect.Width)) + { + UserData = tagTextPair.Key + }; + } + } private void TrySendNetworkUpdate(ISerializableEntity entity, SerializableProperty property) { diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs index f89a52305..0a1abe7ea 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs @@ -372,167 +372,6 @@ namespace Barotrauma } } - private static void UpdateFireSounds(float deltaTime) - { - for (int i = 0; i < fireVolumeLeft.Length; i++) - { - fireVolumeLeft[i] = 0.0f; - fireVolumeRight[i] = 0.0f; - } - - Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y); - foreach (Hull hull in Hull.hullList) - { - foreach (FireSource fs in hull.FireSources) - { - Vector2 diff = fs.WorldPosition + fs.Size / 2 - listenerPos; - if (Math.Abs(diff.X) < FireSoundRange && Math.Abs(diff.Y) < FireSoundRange) - { - Vector2 diffLeft = (fs.WorldPosition + new Vector2(fs.Size.X, fs.Size.Y / 2)) - listenerPos; - if (diff.X < fs.Size.X / 2.0f) diff.X = 0.0f; - if (diffLeft.X <= 0) - { - float distFallOffLeft = diffLeft.Length() / FireSoundRange; - if (distFallOffLeft < 0.99f) - { - fireVolumeLeft[0] += (1.0f - distFallOffLeft) * (fs.Size.X / FireSoundLargeLimit); - if (fs.Size.X > FireSoundLargeLimit) fireVolumeLeft[1] += (1.0f - distFallOffLeft) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit); - } - } - - Vector2 diffRight = (fs.WorldPosition + new Vector2(0.0f, fs.Size.Y / 2)) - listenerPos; - if (diff.X < fs.Size.X / 2.0f) diff.X = 0.0f; - if (diffRight.X >= 0) - { - float distFallOffRight = diffRight.Length() / FireSoundRange; - if (distFallOffRight < 0.99f) - { - fireVolumeRight[0] += 1.0f - distFallOffRight; - if (fs.Size.X > FireSoundLargeLimit) fireVolumeRight[1] += (1.0f - distFallOffRight) * ((fs.Size.X - FireSoundLargeLimit) / FireSoundLargeLimit); - } - } - } - } - } - - for (int i = 0; i < fireVolumeLeft.Length; i++) - { - if (fireVolumeLeft[i] < 0.05f && fireVolumeRight[i] < 0.05f) - { - if (fireSoundChannels[i] != null) - { - fireSoundChannels[i].FadeOutAndDispose(); - fireSoundChannels[i] = null; - } - } - else - { - Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (fireVolumeRight[i] - fireVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y); - if (fireSoundChannels[i] == null || !fireSoundChannels[i].IsPlaying) - { - fireSoundChannels[i] = GetSound(i == 0 ? "fire" : "firelarge").Play(1.0f, FlowSoundRange, soundPos); - fireSoundChannels[i].Looping = true; - } - fireSoundChannels[i].Gain = Math.Max(fireVolumeRight[i], fireVolumeLeft[i]); - fireSoundChannels[i].Position = new Vector3(soundPos, 0.0f); - } - } - } - - if (waterAmbiences.Count > 1) - { - if (waterAmbienceChannels[0] == null || !waterAmbienceChannels[0].IsPlaying) - { - waterAmbienceChannels[0] = waterAmbiences[0].Play(ambienceVolume * (1.0f - movementSoundVolume),"waterambience"); - //waterAmbiences[0].Loop(waterAmbienceIndexes[0], ambienceVolume * (1.0f - movementSoundVolume)); - waterAmbienceChannels[0].Looping = true; - } - else - { - waterAmbienceChannels[0].Gain = ambienceVolume * (1.0f - movementSoundVolume); - } - - if (waterAmbienceChannels[1] == null || !waterAmbienceChannels[1].IsPlaying) - { - waterAmbienceChannels[1] = waterAmbiences[1].Play(ambienceVolume * movementSoundVolume, "waterambience"); - //waterAmbienceIndexes[1] = waterAmbiences[1].Loop(waterAmbienceIndexes[1], ambienceVolume * movementSoundVolume); - waterAmbienceChannels[1].Looping = true; - } - else - { - waterAmbienceChannels[1].Gain = ambienceVolume * movementSoundVolume; - } - } - } - - private static void UpdateWaterFlowSounds(float deltaTime) - { - if (FlowSounds.Count == 0) { return; } - - float[] targetFlowLeft = new float[FlowSounds.Count]; - float[] targetFlowRight = new float[FlowSounds.Count]; - - Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y); - foreach (Gap gap in Gap.GapList) - { - if (gap.Open < 0.01f) continue; - float gapFlow = Math.Abs(gap.LerpedFlowForce.X) + Math.Abs(gap.LerpedFlowForce.Y) * 2.5f; - - if (gapFlow < 10.0f) continue; - - int flowSoundIndex = (int)Math.Floor(MathHelper.Clamp(gapFlow / MaxFlowStrength, 0, FlowSounds.Count)); - flowSoundIndex = Math.Min(flowSoundIndex, FlowSounds.Count - 1); - - Vector2 diff = gap.WorldPosition - listenerPos; - if (Math.Abs(diff.X) < FlowSoundRange && Math.Abs(diff.Y) < FlowSoundRange) - { - float dist = diff.Length(); - float distFallOff = dist / FlowSoundRange; - if (distFallOff >= 0.99f) continue; - - //flow at the left side - if (diff.X < 0) - { - targetFlowLeft[flowSoundIndex] = 1.0f - distFallOff; - } - else - { - targetFlowRight[flowSoundIndex] = 1.0f - distFallOff; - } - } - } - - for (int i = 0; i < FlowSounds.Count; i++) - { - flowVolumeLeft[i] = (targetFlowLeft[i] < flowVolumeLeft[i]) ? - Math.Max(targetFlowLeft[i], flowVolumeLeft[i] - deltaTime) : - Math.Min(targetFlowLeft[i], flowVolumeLeft[i] + deltaTime); - flowVolumeRight[i] = (targetFlowRight[i] < flowVolumeRight[i]) ? - Math.Max(targetFlowRight[i], flowVolumeRight[i] - deltaTime) : - Math.Min(targetFlowRight[i], flowVolumeRight[i] + deltaTime); - - if (flowVolumeLeft[i] < 0.05f && flowVolumeRight[i] < 0.05f) - { - if (flowSoundChannels[i] != null) - { - flowSoundChannels[i].Dispose(); - flowSoundChannels[i] = null; - } - } - else - { - Vector2 soundPos = new Vector2(GameMain.SoundManager.ListenerPosition.X + (flowVolumeRight[i] - flowVolumeLeft[i]) * 100, GameMain.SoundManager.ListenerPosition.Y); - if (flowSoundChannels[i] == null || !flowSoundChannels[i].IsPlaying) - { - flowSoundChannels[i] = FlowSounds[i].Play(1.0f, FlowSoundRange, soundPos); - flowSoundChannels[i].Looping = true; - } - flowSoundChannels[i].Gain = Math.Max(flowVolumeRight[i], flowVolumeLeft[i]); - flowSoundChannels[i].Position = new Vector3(soundPos, 0.0f); - } - } - } - private static void UpdateFireSounds(float deltaTime) { for (int i = 0; i < fireVolumeLeft.Length; i++) diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs b/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs index 3e86fcc2c..18126a8ba 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs @@ -32,6 +32,9 @@ namespace Barotrauma.Sounds public bool UseRadioFilter; public bool UseMuffleFilter; + public float Near { get; private set; } + public float Far { get; private set; } + private static BiQuad[] muffleFilters = new BiQuad[] { new LowpassFilter(VoipConfig.FREQUENCY, 800) @@ -41,6 +44,16 @@ namespace Barotrauma.Sounds new BandpassFilter(VoipConfig.FREQUENCY, 2000) }; + public float Gain + { + get { return soundChannel == null ? 0.0f : soundChannel.Gain; } + set + { + if (soundChannel == null) { return; } + soundChannel.Gain = value; + } + } + public VoipSound(SoundManager owner, VoipQueue q) : base(owner, "voip", true, true) { VoipConfig.SetupEncoding(); @@ -64,8 +77,8 @@ namespace Barotrauma.Sounds public void SetRange(float near, float far) { - soundChannel.Near = near; - soundChannel.Far = far; + soundChannel.Near = Near = near; + soundChannel.Far = Far = far; } public void ApplyFilters(short[] buffer, int readSamples) diff --git a/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs b/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs index 67e912b03..a9ad8e446 100644 --- a/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs +++ b/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs @@ -151,6 +151,7 @@ namespace Barotrauma for (int i = traitStart + NPCPersonalityTrait.List.Count; i < csvContent.Length; i++) // Conversations { + string[] presplit = csvContent[i].Split(','); // Handling speaker index fetching, somehow doesn't work with the regex string[] split = SplitCSV(csvContent[i]); int emptyFields = 0; @@ -172,20 +173,20 @@ namespace Barotrauma continue; } - string speaker = split[1]; - int depthIndex = int.Parse(split[2]); + string speaker = presplit[1]; + int depthIndex = int.Parse(presplit[2]); // 3 = original line string line = split[4].Replace("\"", ""); string flags = split[5].Replace("\"", ""); string allowedJobs = split[6].Replace("\"", ""); string speakerTags = split[7].Replace("\"", ""); - string minIntensity = split[8].Replace("\"", ""); - string maxIntensity = split[9].Replace("\"", ""); + string minIntensity = split[8].Replace("\"", "").Replace(",", "."); + string maxIntensity = split[9].Replace("\"", "").Replace(",", "."); string element = $"{GetIndenting(depthIndex)}" + $" 0.0f || IsDead); tempBuffer.Write(AnimController.Dir > 0.0f); } diff --git a/Barotrauma/BarotraumaServer/Source/GameMain.cs b/Barotrauma/BarotraumaServer/Source/GameMain.cs index 6d44fd936..53e3623d6 100644 --- a/Barotrauma/BarotraumaServer/Source/GameMain.cs +++ b/Barotrauma/BarotraumaServer/Source/GameMain.cs @@ -83,6 +83,7 @@ namespace Barotrauma LevelGenerationParams.LoadPresets(); ScriptedEventSet.LoadPrefabs(); + AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions)); StructurePrefab.LoadAll(GetFilesOfType(ContentType.Structure)); ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item)); JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs)); @@ -90,7 +91,6 @@ namespace Barotrauma NPCConversation.LoadAll(GetFilesOfType(ContentType.NPCConversations)); ItemAssemblyPrefab.LoadAll(); LevelObjectPrefab.LoadAll(); - AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions)); GameModePreset.Init(); LocationType.Init(); diff --git a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs index 0e1667db4..7cacaa130 100644 --- a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -168,6 +168,8 @@ namespace Barotrauma msg.Write(isRunning && endWatchman != null ? endWatchman.ID : (UInt16)0); msg.Write(Money); + msg.Write(PurchasedHullRepairs); + msg.Write(PurchasedItemRepairs); msg.Write((UInt16)CargoManager.PurchasedItems.Count); foreach (PurchasedItem pi in CargoManager.PurchasedItems) @@ -192,6 +194,8 @@ namespace Barotrauma { UInt16 selectedLocIndex = msg.ReadUInt16(); byte selectedMissionIndex = msg.ReadByte(); + bool purchasedHullRepairs = msg.ReadBoolean(); + bool purchasedItemRepairs = msg.ReadBoolean(); UInt16 purchasedItemCount = msg.ReadUInt16(); List purchasedItems = new List(); @@ -208,6 +212,17 @@ namespace Barotrauma return; } + if (purchasedHullRepairs && !this.PurchasedHullRepairs && Money >= HullRepairCost) + { + this.PurchasedHullRepairs = true; + Money -= HullRepairCost; + } + if (purchasedItemRepairs && !this.PurchasedItemRepairs && Money >= ItemRepairCost) + { + this.PurchasedItemRepairs = true; + Money -= ItemRepairCost; + } + Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); if (Map.SelectedConnection != null) { diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index d4c20614b..d9137afc7 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -1754,11 +1754,10 @@ namespace Barotrauma.Networking Log("Game mode: " + selectedMode.Name, ServerLog.MessageType.ServerMessage); Log("Submarine: " + selectedSub.Name, ServerLog.MessageType.ServerMessage); Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, ServerLog.MessageType.ServerMessage); - } + } - bool missionAllowRespawn = campaign == null && - (!(GameMain.GameSession.GameMode is MissionMode) || - ((MissionMode)GameMain.GameSession.GameMode).Mission.AllowRespawn); + MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode; + bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn); if (serverSettings.AllowRespawn && missionAllowRespawn) respawnManager = new RespawnManager(this, usingShuttle ? selectedShuttle : null); @@ -1767,6 +1766,12 @@ namespace Barotrauma.Networking { var teamID = n == 0 ? Character.TeamType.Team1 : Character.TeamType.Team2; + Submarine.MainSubs[n].TeamID = teamID; + foreach (Submarine sub in Submarine.MainSubs[n].DockedTo) + { + sub.TeamID = teamID; + } + //find the clients in this team List teamClients = teamCount == 1 ? new List(connectedClients) : @@ -1935,10 +1940,8 @@ namespace Barotrauma.Networking MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; - bool missionAllowRespawn = campaign == null && - (!(GameMain.GameSession.GameMode is MissionMode) || - ((MissionMode)GameMain.GameSession.GameMode).Mission.AllowRespawn); - + MissionMode missionMode = GameMain.GameSession.GameMode as MissionMode; + bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn); msg.Write(serverSettings.AllowRespawn && missionAllowRespawn); msg.Write(Submarine.MainSubs[1] != null); //loadSecondSub diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index 326df08c3..d1d613a94 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -10,9 +10,7 @@ namespace Barotrauma.Networking class ServerEntityEvent : NetEntityEvent { private IServerSerializable serializable; - - public bool Sent; - + #if DEBUG public string StackTrace; #endif diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index 529a93864..5d1bf37a2 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -5,11 +5,14 @@ - - - + + + + + + + - diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index 7fae3d1c5..87350b127 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -24,6 +24,10 @@ + + + + @@ -357,6 +361,81 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -591,94 +670,94 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest @@ -1301,7 +1380,7 @@ PreserveNewest - + PreserveNewest @@ -1568,7 +1647,7 @@ PreserveNewest - + PreserveNewest @@ -1580,10 +1659,7 @@ PreserveNewest - - PreserveNewest - - + PreserveNewest @@ -1592,10 +1668,10 @@ PreserveNewest - + PreserveNewest - + PreserveNewest @@ -1928,6 +2004,27 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 88d29d8c9..965a0d4d8 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -12,6 +12,8 @@ namespace Barotrauma { partial class EnemyAIController : AIController { + public static bool DisableEnemyAI; + class WallTarget { public Vector2 Position; @@ -249,7 +251,7 @@ namespace Barotrauma } } - public TargetingPriority GetTargetingPriority(string targetTag) + private TargetingPriority GetTargetingPriority(string targetTag) { if (targetingPriorities.TryGetValue(targetTag, out TargetingPriority priority)) { @@ -485,7 +487,7 @@ namespace Barotrauma } else { - if (!IsProperlyLatched) + if (!IsProperlyLatchedOnSub) { UpdateWallTarget(); } @@ -1001,7 +1003,7 @@ namespace Barotrauma private void UpdateEating(float deltaTime) { - if (SelectedAiTarget == null) + if (SelectedAiTarget == null) //SelectedAiTarget.Entity is Character c && !c.IsDead { State = AIState.Idle; return; @@ -1038,14 +1040,14 @@ namespace Barotrauma #region Targeting - private bool IsProperlyLatched => LatchOntoAI != null && LatchOntoAI.IsAttached && SelectedAiTarget?.Entity == wallTarget?.Structure; + private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; //goes through all the AItargets, evaluates how preferable it is to attack the target, //whether the Character can see/hear the target and chooses the most preferable target within //sight/hearing range public AITarget UpdateTargets(Character character, out TargetingPriority priority) { - if (IsProperlyLatched) + if (IsProperlyLatchedOnSub) { // If attached to a valid target, just keep the target. // Priority not used in this case. diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index f41af8814..41b183cec 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -267,7 +267,7 @@ namespace Barotrauma if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime)) { Character.Speak( - newOrder.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: false), ChatMessageType.Order); + newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order); } } } @@ -286,7 +286,7 @@ namespace Barotrauma if (Character.PressureTimer > 50.0f && Character.CurrentHull != null) { - Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f); + Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.DisplayName), null, 0, "pressure", 30.0f); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs index b2ece3f38..e1109f83f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs @@ -388,6 +388,18 @@ namespace Barotrauma buttonPressCooldown = ButtonPressInterval; break; } + else + { + if (!door.HasRequiredItems(character, false) && shouldBeOpen) + { + currentPath.Unreachable = true; + return; + } + + door.Item.TryInteract(character, false, true, true); + buttonPressCooldown = ButtonPressInterval; + break; + } } } } @@ -414,20 +426,12 @@ namespace Barotrauma if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { return null; } } - if (!canBreakDoors) - { - //door closed and the character can't open doors -> node can't be traversed - if (!canOpenDoors || character.LockHands) return null; - - var doorButtons = nextNode.Waypoint.ConnectedDoor.Item.GetConnectedComponents(); - if (!doorButtons.Any()) return null; - foreach (Controller button in doorButtons) { if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) != - Math.Sign(node.Position.X - nextNode.Position.X)) continue; + Math.Sign(node.Position.X - nextNode.Position.X)) { continue; } - if (!button.HasRequiredItems(character, false)) return null; + if (!button.HasRequiredItems(character, false)) { return null; } } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs index fe5304a4c..c85b2caa7 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs @@ -54,6 +54,8 @@ namespace Barotrauma get { return attachJoints.Count > 0; } } + public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null); + public LatchOntoAI(XElement element, EnemyAIController enemyAI) { attachToWalls = element.GetAttributeBool("attachtowalls", false); @@ -207,10 +209,10 @@ namespace Barotrauma break; } - if (attachTargetBody != null && deattachTimer < 0.0f) + if (IsAttached && attachTargetBody != null && deattachTimer < 0.0f) { Entity entity = attachTargetBody.UserData as Entity; - Submarine attachedSub = entity is Submarine ? (Submarine)entity : entity?.Submarine; + Submarine attachedSub = entity is Submarine sub ? sub : entity?.Submarine; if (attachedSub != null) { float velocity = attachedSub.Velocity == Vector2.Zero ? 0.0f : attachedSub.Velocity.Length(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 6b7dda849..9acf4bc99 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -44,6 +44,7 @@ namespace Barotrauma private AIObjectiveContainItem reloadWeaponObjective; private Hull retreatTarget; private AIObjectiveGoTo retreatObjective; + private AIObjectiveFindSafety findSafety; private float coolDownTimer; @@ -60,7 +61,9 @@ namespace Barotrauma { Enemy = enemy; coolDownTimer = CoolDown; - HumanAIController.ObjectiveManager.GetObjective().Priority = 0; + findSafety = HumanAIController.ObjectiveManager.GetObjective(); + findSafety.Priority = 0; + findSafety.unreachable.Clear(); Mode = mode; if (Enemy == null) { @@ -175,7 +178,7 @@ namespace Barotrauma { if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted)) { - retreatTarget = HumanAIController.ObjectiveManager.GetObjective().FindBestHull(new List() { character.CurrentHull }); + retreatTarget = findSafety.FindBestHull(new List() { character.CurrentHull }); } if (retreatTarget != null) { @@ -235,6 +238,7 @@ namespace Barotrauma { if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range) { + character.SetInput(InputType.Shoot, false, true); Weapon.Use(deltaTime, character); } } @@ -264,6 +268,7 @@ namespace Barotrauma } if (target != null && target == Enemy) { + character.SetInput(InputType.Shoot, false, true); Weapon.Use(deltaTime, character); } } @@ -275,7 +280,6 @@ namespace Barotrauma { abandon = true; SteeringManager.Reset(); - //HumanAIController.ObjectiveManager.GetObjective().Priority = 100; } public override bool IsCompleted() diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 2ac3ef0ce..6b735f0f5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -18,7 +18,7 @@ namespace Barotrauma const float SearchHullInterval = 3.0f; const float clearUnreachableInterval = 30; - private List unreachable = new List(); + public readonly List unreachable = new List(); private float currenthullSafety; private float unreachableClearTimer; @@ -60,11 +60,16 @@ namespace Barotrauma else { divingGearObjective = null; - // Reset the timer so that we get a safe hull target. - searchHullTimer = 0; + // Reduce the timer so that we get a safe hull target faster. + searchHullTimer = Math.Min(1, searchHullTimer); } } + if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD) + { + searchHullTimer = Math.Min(1, searchHullTimer); + } + if (unreachableClearTimer > 0) { unreachableClearTimer -= deltaTime; @@ -188,10 +193,17 @@ namespace Barotrauma float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist)); hullSafety *= distanceFactor; + //skip the hull if the safety is already less than the best hull + //(no need to do the expensive pathfinding if we already know we're not going to choose this hull) + if (hullSafety < bestValue) { continue; } // Each unsafe node reduces the hull safety value. // Ignore current hull, because otherwise the would block all paths from the current hull to the target hull. var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); - if (path.Unreachable) { continue; } + if (path.Unreachable) + { + unreachable.Add(hull); + continue; + } int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull)); hullSafety /= 1 + unsafeNodes; // If the target is not inside a friendly submarine, considerably reduce the hull safety. @@ -219,7 +231,6 @@ namespace Barotrauma } } } - // Huge preference for closer targets float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition); float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance)); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index 865f18203..c03fbef43 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -26,6 +26,9 @@ namespace Barotrauma private float standStillTimer; private float walkDuration; + private readonly List targetHulls = new List(20); + private readonly List hullWeights = new List(20); + public AIObjectiveIdle(Character character) : base(character, "") { standStillTimer = Rand.Range(-10.0f, 10.0f); @@ -106,7 +109,7 @@ namespace Barotrauma if (isCurrentHullOK) { // Check that there is no unsafe or forbidden hulls on the way to the target - // Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull. + // Only do this when the current hull is ok, because otherwise would block all paths from the current hull to the target hull. var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition); if (path.Unreachable || path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) @@ -128,8 +131,8 @@ namespace Barotrauma character.AIController.SelectTarget(currentTarget.AiTarget); string errorMsg = null; #if DEBUG - bool isRoomNameFound = currentTarget.RoomName != null; - errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.RoomName : currentTarget.ToString()) + ")"; + bool isRoomNameFound = currentTarget.DisplayName != null; + errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")"; #endif var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsg); PathSteering.SetPath(path); @@ -230,13 +233,9 @@ namespace Barotrauma } } - private readonly List targetHulls = new List(20); - private readonly List hullWeights = new List(20); - private void FindTargetHulls() { bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull); - targetHulls.Clear(); hullWeights.Clear(); foreach (var hull in Hull.hullList) @@ -266,7 +265,6 @@ namespace Barotrauma hullWeights.Add(weight); } } - } private bool IsForbidden(Hull hull) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs index 943d7110d..72004ee14 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -83,7 +83,7 @@ namespace Barotrauma if (character.SelectedCharacter == null) { character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget") - .Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.RoomName), + .Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName), null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index f1e3c96d7..418c8e68e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -527,7 +527,12 @@ namespace Barotrauma Limb leftLeg = GetLimb(LimbType.LeftLeg); Limb rightLeg = GetLimb(LimbType.RightLeg); - + + float limpAmount = + character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) + + character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true); + limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f); + float walkCycleMultiplier = 1.0f; if (Stairs != null) { @@ -556,13 +561,17 @@ namespace Barotrauma float walkPosX = (float)Math.Cos(WalkPos); float walkPosY = (float)Math.Sin(WalkPos); - - + Vector2 stepSize = StepSize.Value; stepSize.X *= walkPosX; - stepSize.Y *= walkPosY; + stepSize.Y *= walkPosY; - float footMid = colliderPos.X;// (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f; + float footMid = colliderPos.X; + if (limpAmount > 0.0f) + { + //make the footpos oscillate when limping + footMid += ((float)Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * 0.3f); + } movement = overrideTargetMovement == Vector2.Zero ? MathUtils.SmoothStep(movement, TargetMovement, movementLerp) : @@ -576,7 +585,7 @@ namespace Barotrauma movement.Y = 0.0f; if (torso == null) { return; } - + bool isNotRemote = true; if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) isNotRemote = !character.IsRemotePlayer; @@ -680,7 +689,7 @@ namespace Barotrauma //make the character limp if the feet are damaged float footAfflictionStrength = character.CharacterHealth.GetAfflictionStrength("damage", foot, true); - footPos *= MathHelper.Lerp(1.0f, 0.5f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f)); + footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f)); if (onSlope && Stairs == null) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs index 3f32ca43d..81677d50f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Params/Ragdoll/RagdollParams.cs @@ -84,6 +84,7 @@ namespace Barotrauma var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty); if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default") { + //DebugConsole.NewMessage("[RagollParams] Using the default folder."); folder = GetDefaultFolder(speciesName); } return folder; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index a1c3bc6a6..8eaca9be2 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -692,7 +692,7 @@ namespace Barotrauma ImpactProjSpecific(impact, f1.Body); } - public void SeverLimbJoint(LimbJoint limbJoint) + public void SeverLimbJoint(LimbJoint limbJoint, bool playSound = true) { if (!limbJoint.CanBeSevered || limbJoint.IsSevered) { @@ -721,7 +721,7 @@ namespace Barotrauma } } - partial void SeverLimbJointProjSpecific(LimbJoint limbJoint); + partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound = true); private void GetConnectedLimbs(List connectedLimbs, List checkedJoints, Limb limb) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 1f500d2ac..9032c5e8c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -1103,14 +1103,14 @@ namespace Barotrauma if (leftFoot != null) { float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", leftFoot, true); - speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f)); + speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f)); } var rightFoot = AnimController.GetLimb(LimbType.RightFoot); if (rightFoot != null) { float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", rightFoot, true); - speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f)); + speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f)); } return speed; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index 9a25fcd52..c492667e0 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs @@ -231,7 +231,8 @@ namespace Barotrauma : afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter))); } - private LimbHealth GetMathingLimbHealth(Affliction affliction) => limbHealths[Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb).HealthIndex]; + private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex]; + private LimbHealth GetMathingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb)); /// /// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb. diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index 3cdabed81..d52d022ce 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -255,8 +255,10 @@ namespace Barotrauma commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) => { - ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma."); - return; + HumanAIController.DisableCrewAI = true; + NewMessage("Crew AI disabled", Color.Red); + // This is probably not where it should be? + //ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma."); /*if (GameMain.Server == null) return; GameMain.Server.KarmaEnabled = !GameMain.Server.KarmaEnabled;*/ })); @@ -264,7 +266,19 @@ namespace Barotrauma commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) => { HumanAIController.DisableCrewAI = false; - NewMessage("Crew AI enabled", Color.White); + NewMessage("Crew AI enabled", Color.Green); + }, isCheat: true)); + + commands.Add(new Command("disableenemyai", "disableenemyai: Disable the AI of the Enemy characters (monsters).", (string[] args) => + { + EnemyAIController.DisableEnemyAI = true; + NewMessage("Enemy AI disabled", Color.Red); + }, isCheat: true)); + + commands.Add(new Command("enableenemyai", "enableenemyai: Enable the AI of the Enemy characters (monsters).", (string[] args) => + { + EnemyAIController.DisableEnemyAI = false; + NewMessage("Enemy AI enabled", Color.Green); }, isCheat: true)); commands.Add(new Command("botcount", "botcount [x]: Set the number of bots in the crew in multiplayer.", null)); diff --git a/Barotrauma/BarotraumaShared/Source/Events/MonsterEvent.cs b/Barotrauma/BarotraumaShared/Source/Events/MonsterEvent.cs index e8b6d528a..c5db6fa09 100644 --- a/Barotrauma/BarotraumaShared/Source/Events/MonsterEvent.cs +++ b/Barotrauma/BarotraumaShared/Source/Events/MonsterEvent.cs @@ -209,6 +209,7 @@ namespace Barotrauma //isActive = false; + bool spawnReady = false; if (spawnPending) { //wait until there are no submarines at the spawnpos @@ -219,25 +220,31 @@ namespace Barotrauma if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos) < minDist * minDist) return; } + spawnPending = false; + //+1 because Range returns an integer less than the max value int amount = Rand.Range(minAmount, maxAmount + 1, Rand.RandSync.Server); - monsters = new Character[amount]; - + monsters = new List(); + float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100; for (int i = 0; i < amount; i++) - { - bool isClient = false; + { + CoroutineManager.InvokeAfter(() => + { + bool isClient = false; #if CLIENT - isClient = GameMain.Client != null; + isClient = GameMain.Client != null; #endif - - monsters[i] = Character.Create( - characterFile, spawnPos + Rand.Vector(100.0f, Rand.RandSync.Server), - i.ToString(), null, isClient, true, true); + monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true)); + if (monsters.Count == amount) + { + spawnReady = true; + } + }, Rand.Range(0f, amount / 2, Rand.RandSync.Server)); } - - spawnPending = false; } + if (!spawnReady) { return; } + Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter); #if CLIENT if (Character.Controlled != null) targetEntity = (Entity)Character.Controlled; diff --git a/Barotrauma/BarotraumaShared/Source/Extensions/StringFormatter.cs b/Barotrauma/BarotraumaShared/Source/Extensions/StringFormatter.cs index 541879e2d..9aa6cdada 100644 --- a/Barotrauma/BarotraumaShared/Source/Extensions/StringFormatter.cs +++ b/Barotrauma/BarotraumaShared/Source/Extensions/StringFormatter.cs @@ -8,6 +8,22 @@ namespace Barotrauma { public static class StringFormatter { + public static string Replace(this string s, string replacement, Func predicate) + { + var newString = new string[s.Length]; + for (int i = 0; i < s.Length; i++) + { + char letter = s[i]; + string newLetter = letter.ToString(); + if (predicate(letter)) + { + newLetter = replacement; + } + newString[i] = newLetter; + } + return new string(newString.SelectMany(str => str.ToCharArray()).ToArray()); + } + public static string Remove(this string s, Func predicate) { return new string(s.ToCharArray().Where(c => !predicate(c)).ToArray()); diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs b/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs index aa0aa8fda..271dd6dc1 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/CargoManager.cs @@ -108,7 +108,7 @@ namespace Barotrauma } #if CLIENT - new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.RoomName)); + new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.DisplayName)); #endif Dictionary availableContainers = new Dictionary(); diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs index 220e756cd..f081d1c0e 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs @@ -13,6 +13,7 @@ namespace Barotrauma public bool CheatsEnabled; const int InitialMoney = 4700; + public const int HullRepairCost = 500, ItemRepairCost = 500; protected bool watchmenSpawned; protected Character startWatchman, endWatchman; @@ -20,6 +21,8 @@ namespace Barotrauma //key = dialog flag, double = Timing.TotalTime when the line was last said private Dictionary dialogLastSpoken = new Dictionary(); + public bool PurchasedHullRepairs, PurchasedItemRepairs; + protected Map map; public Map Map { @@ -70,6 +73,37 @@ namespace Barotrauma watchmenSpawned = false; startWatchman = null; endWatchman = null; + + if (PurchasedHullRepairs) + { + foreach (Structure wall in Structure.WallList) + { + if (wall.Submarine == null || wall.Submarine.IsOutpost) { continue; } + if (wall.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(wall.Submarine)) + { + for (int i = 0; i < wall.SectionCount; i++) + { + wall.AddDamage(i, -100000.0f); + } + } + } + PurchasedHullRepairs = false; + } + if (PurchasedItemRepairs) + { + foreach (Item item in Item.ItemList) + { + if (item.Submarine == null || item.Submarine.IsOutpost) { continue; } + if (item.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(item.Submarine)) + { + if (item.GetComponent() != null) + { + item.Condition = item.Health; + } + } + } + PurchasedItemRepairs = false; + } } public override void Update(float deltaTime) diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs index 53d1d1ead..c555982f3 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs @@ -44,6 +44,7 @@ namespace Barotrauma public bool ChromaticAberrationEnabled { get; set; } public bool MuteOnFocusLost { get; set; } + public bool UseDirectionalVoiceChat { get; set; } public enum VoiceMode { @@ -149,6 +150,9 @@ namespace Barotrauma public bool EnableMouseLook { get; set; } = true; + public bool CrewMenuOpen { get; set; } = true; + public bool ChatOpen { get; set; } = true; + private bool unsavedSettings; public bool UnsavedSettings { @@ -826,6 +830,8 @@ namespace Barotrauma SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume); MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume); VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume); + MuteOnFocusLost = audioSettings.GetAttributeBool("muteonfocuslost", false); + UseDirectionalVoiceChat = audioSettings.GetAttributeBool("usedirectionalvoicechat", true); string voiceSettingStr = audioSettings.GetAttributeString("voicesetting", "Disabled"); VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", ""); NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", -45); @@ -844,6 +850,9 @@ namespace Barotrauma AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount); EnableMouseLook = doc.Root.GetAttributeBool("enablemouselook", EnableMouseLook); + CrewMenuOpen = doc.Root.GetAttributeBool("crewmenuopen", CrewMenuOpen); + ChatOpen = doc.Root.GetAttributeBool("chatopen", ChatOpen); + foreach (XElement subElement in doc.Root.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) @@ -1019,7 +1028,9 @@ namespace Barotrauma new XAttribute("requiresteamauthentication", requireSteamAuthentication), new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems), new XAttribute("aimassistamount", aimAssistAmount), - new XAttribute("enablemouselook", EnableMouseLook)); + new XAttribute("enablemouselook", EnableMouseLook), + new XAttribute("chatopen", ChatOpen), + new XAttribute("crewmenuopen", CrewMenuOpen)); if (!ShowUserStatisticsPrompt) { @@ -1055,6 +1066,8 @@ namespace Barotrauma audio.ReplaceAttributes( new XAttribute("musicvolume", musicVolume), new XAttribute("soundvolume", soundVolume), + new XAttribute("muteonfocuslost", MuteOnFocusLost), + new XAttribute("usedirectionalvoicechat", UseDirectionalVoiceChat), new XAttribute("voicesetting", VoiceSetting), new XAttribute("voicecapturedevice", VoiceCaptureDevice ?? ""), new XAttribute("noisegatethreshold", NoiseGateThreshold)); diff --git a/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs index c3800f565..4b8d00255 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs @@ -56,7 +56,7 @@ namespace Barotrauma switch (SlotTypes[i]) { //case InvSlotType.Head: - case InvSlotType.OuterClothes: + //case InvSlotType.OuterClothes: case InvSlotType.LeftHand: case InvSlotType.RightHand: hideEmptySlot[i] = true; @@ -176,6 +176,9 @@ namespace Barotrauma { if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item) { +#if CLIENT + if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; } +#endif if (!Items[i].AllowedSlots.Contains(InvSlotType.Any) || !TryPutItem(Items[i], character, new List { InvSlotType.Any }, true)) { free = false; @@ -195,6 +198,9 @@ namespace Barotrauma { if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] == null) { +#if CLIENT + if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; } +#endif bool removeFromOtherSlots = item.ParentInventory != this; if (placedInSlot == -1 && inWrongSlot) { @@ -248,7 +254,9 @@ namespace Barotrauma GameAnalyticsManager.AddErrorEventOnce("CharacterInventory.TryPutItem:IndexOutOfRange", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return false; } - +#if CLIENT + if (PersonalSlots.HasFlag(SlotTypes[index])) { hidePersonalSlots = false; } +#endif //there's already an item in the slot if (Items[index] != null) { @@ -273,7 +281,9 @@ namespace Barotrauma foreach (InvSlotType allowedSlot in allowedSlots) { if (!allowedSlot.HasFlag(SlotTypes[index])) continue; - +#if CLIENT + if (PersonalSlots.HasFlag(allowedSlot)) { hidePersonalSlots = false; } +#endif for (int i = 0; i < capacity; i++) { if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs index f26db9c0c..973b7e655 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs @@ -221,29 +221,64 @@ namespace Barotrauma.Items.Components { if (item.Condition <= RepairThreshold) return true; //For repairing + var idCard = character.Inventory.FindItemByIdentifier("idcard"); + hasValidIdCard = requiredItems.Any(ri => ri.Value.Any(r => r.MatchesItem(idCard))); + Msg = requiredItems.None() || hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar"; + ParseMsg(); + if (addMessage) + { + msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText); + } + //this is a bit pointless atm because if canBePicked is false it won't allow you to do Pick() anyway, however it's still good for future-proofing. return requiredItems.Any() ? base.HasRequiredItems(character, addMessage, msg) : canBePicked; } public override bool Pick(Character picker) { - return item.Condition <= RepairThreshold ? true : base.Pick(picker); + if (item.Condition <= RepairThreshold) { return true; } + if (requiredItems.None()) { return false; } + if (HasRequiredItems(picker, false) && hasValidIdCard) { return false; } + return base.Pick(picker); } public override bool OnPicked(Character picker) { if (item.Condition <= RepairThreshold) return true; //repairs + if (requiredItems.Any() && !hasValidIdCard) + { + ForceOpen(ActionType.OnPicked); + } + return false; + } + private void ForceOpen(ActionType actionType) + { SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function #if CLIENT - PlaySound(ActionType.OnPicked, item.WorldPosition, picker); + PlaySound(actionType, item.WorldPosition, picker); #endif } public override bool Select(Character character) { //can only be selected if the item is broken - return item.Condition <= RepairThreshold; + if (item.Condition <= RepairThreshold) return true; //repairs + bool hasRequiredItems = HasRequiredItems(character, false); + if (requiredItems.None() || hasRequiredItems && hasValidIdCard) + { + float originalPickingTime = PickingTime; + PickingTime = 0; + ForceOpen(ActionType.OnUse); + PickingTime = originalPickingTime; + } + else if (hasRequiredItems) + { +#if CLIENT + GUI.AddMessage(accessDeniedTxt, Color.Red); +#endif + } + return false; } public override void Update(float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ElectricalDischarger.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ElectricalDischarger.cs index 501491c11..001155e91 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ElectricalDischarger.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ElectricalDischarger.cs @@ -34,6 +34,20 @@ namespace Barotrauma.Items.Components } } + public override bool IsActive + { + get { return base.IsActive; } + set + { + base.IsActive = value; + if (!value) + { + nodes.Clear(); + charactersInRange.Clear(); + } + } + } + [Serialize(100.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)] public float Range { @@ -126,14 +140,19 @@ namespace Barotrauma.Items.Components } else { - nodes.Clear(); - charactersInRange.Clear(); IsActive = false; } voltage = 0.0f; } + public override void UpdateBroken(float deltaTime, Camera cam) + { + base.UpdateBroken(deltaTime, cam); + nodes.Clear(); + charactersInRange.Clear(); + } + private void Discharge() { ApplyStatusEffects(ActionType.OnUse, 1.0f); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs index 2b38622e6..ffb107200 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Holdable.cs @@ -58,13 +58,6 @@ namespace Barotrauma.Items.Components set; } - [Serialize(true, true)] - public bool Aimable - { - get; - set; - } - [Serialize(false, false)] public bool ControlPose { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs index 90a7a0665..d557dd19d 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/LevelResource.cs @@ -11,6 +11,12 @@ namespace Barotrauma.Items.Components { private float lastSentDeattachTimer; + private PhysicsBody trigger; + + private Holdable holdable; + + private float deattachTimer; + [Serialize(1.0f, false)] public float DeattachDuration { @@ -49,13 +55,7 @@ namespace Barotrauma.Items.Components #endif } } - - private PhysicsBody trigger; - - private Holdable holdable; - - private float deattachTimer; - + public LevelResource(Item item, XElement element) : base(item, element) { IsActive = true; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs index 48f55e7df..69aa69502 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Pickable.cs @@ -142,8 +142,6 @@ namespace Barotrauma.Items.Components pickTimer / requiredTime, Color.Red, Color.Green); #endif - - picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f)); picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f)); pickTimer += CoroutineManager.DeltaTime; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index 08cef0492..38953e28a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -322,6 +322,7 @@ namespace Barotrauma.Items.Components // If the character is climbing, ignore the check, because we cannot aim while climbing. if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4) { + character.SetInput(InputType.Shoot, false, true); Use(deltaTime, character); } else @@ -337,11 +338,11 @@ namespace Barotrauma.Items.Components sinTime = 0; if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f)) { - character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f); + character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leaksfixed", 10.0f); } else { - character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leakfixed", 10.0f); + character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leakfixed", 10.0f); } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs index af7073f5c..50d9d7ce1 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs @@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components get { return name; } } - [Editable, Serialize("", true)] + [Editable, Serialize("", true, translationTextTag: "ItemMsg")] public string Msg { get; @@ -538,7 +538,6 @@ namespace Barotrauma.Items.Components GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return 0.0f; } - float average = skillSuccessSum / requiredSkills.Count; float skillSuccessSum = 0.0f; for (int i = 0; i < requiredSkills.Count; i++) @@ -555,7 +554,7 @@ namespace Barotrauma.Items.Components public virtual void FlipY(bool relativeToSub) { } - public bool HasRequiredContainedItems(bool addMessage) + public bool HasRequiredContainedItems(bool addMessage, string msg = null) { if (!requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) return true; if (item.OwnInventory == null) return false; @@ -582,33 +581,52 @@ namespace Barotrauma.Items.Components { if (!requiredItems.Any()) return true; if (character.Inventory == null) return false; - + bool hasRequiredItems = false; + bool canContinue = true; if (requiredItems.ContainsKey(RelatedItem.RelationType.Equipped)) { foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Equipped]) { - if (character.SelectedItems.FirstOrDefault(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)) == null) + canContinue = CheckItems(ri, character.SelectedItems); + if (!canContinue) { break; } + } + } + if (canContinue) + { + if (requiredItems.ContainsKey(RelatedItem.RelationType.Picked)) + { + foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Picked]) { -#if CLIENT - if (addMessage && !string.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red); -#endif - return false; + if (!CheckItems(ri, character.Inventory.Items)) { break; } } } } - if (requiredItems.ContainsKey(RelatedItem.RelationType.Picked)) - { - foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Picked]) - { - if (character.Inventory.Items.FirstOrDefault(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)) == null) - { + #if CLIENT if (!hasRequiredItems && addMessage && !string.IsNullOrEmpty(msg)) { GUI.AddMessage(msg, Color.Red); } #endif - return false; + return hasRequiredItems; + + bool CheckItems(RelatedItem relatedItem, IEnumerable itemList) + { + bool Predicate(Item it) => it != null && it.Condition > 0.0f && relatedItem.MatchesItem(it); + bool shouldBreak = false; + if (relatedItem.IsOptional) + { + if (!hasRequiredItems) + { + hasRequiredItems = itemList.Any(Predicate); + } + } + else + { + hasRequiredItems = itemList.Any(Predicate); + if (!hasRequiredItems) + { + shouldBreak = true; } } if (!hasRequiredItems) @@ -620,8 +638,6 @@ namespace Barotrauma.Items.Components } return !shouldBreak; } - - return true; } public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Character user = null) @@ -766,6 +782,7 @@ namespace Barotrauma.Items.Components { newRequiredItem.statusEffects = prevRequiredItem.statusEffects; newRequiredItem.Msg = prevRequiredItem.Msg; + newRequiredItem.IsOptional = prevRequiredItem.IsOptional; } if (!requiredItems.ContainsKey(newRequiredItem.Type)) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs index 9c022b67b..69a209f97 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs @@ -343,5 +343,7 @@ namespace Barotrauma.Items.Components limbPositions[i] = new LimbPos(limbPositions[i].limbType, flippedPos); } } + + partial void HideHUDs(bool value); } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs index 1fd9ca9e6..48b98b710 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs @@ -8,11 +8,19 @@ namespace Barotrauma.Items.Components { partial class CustomInterface : ItemComponent, IClientSerializable, IServerSerializable { - class CustomInterfaceElement + class CustomInterfaceElement : ISerializableEntity { public bool ContinuousSignal; public bool State; - public string Label, Connection, Signal; + public string Connection; + [Serialize("", false, translationTextTag = "Label.")] + public string Label { get; set; } + [Serialize("1", false)] + public string Signal { get; set; } + + public string Name => "CustomInterfaceElement"; + + public Dictionary SerializableProperties { get; set; } public List StatusEffects = new List(); @@ -33,7 +41,7 @@ namespace Barotrauma.Items.Components } private string[] labels; - [Serialize("", true), Editable()] + [Serialize("", true)] public string Labels { get { return string.Join(",", labels); } @@ -48,7 +56,7 @@ namespace Barotrauma.Items.Components } } private string[] signals; - [Serialize("", true), Editable()] + [Serialize("", true)] public string Signals { //use semicolon as a separator because comma may be needed in the signals (for color or vector values for example) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs index 4bee0e644..1befb10a5 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Wearable.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Xml.Linq; using Barotrauma.Items.Components; +using Barotrauma.Extensions; namespace Barotrauma { @@ -66,6 +67,8 @@ namespace Barotrauma public LightComponent LightComponent { get; set; } + public int Variant { get; set; } + private Gender _gender; /// /// None = Any/Not Defined -> no effect. @@ -112,30 +115,65 @@ namespace Barotrauma /// /// Note: this constructor cannot initialize automatically, because the gender is unknown at this point. We only know it when the item is equipped. /// - public WearableSprite(XElement subElement, Wearable wearable) + public WearableSprite(XElement subElement, Wearable wearable, int variant = 0) { Type = WearableType.Item; WearableComponent = wearable; + Variant = Math.Max(variant, 0); SpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty)); SourceElement = subElement; } private string ParseSpritePath(string texturePath) => texturePath.Contains("/") ? texturePath : $"{Path.GetDirectoryName(WearableComponent.Item.Prefab.ConfigFile)}/{texturePath}"; + public void RefreshPath() + { + if (Variant > 0) + { + // Restore the tag so that we can parse it again. + ReplaceNumbersWith("[VARIANT]"); + } + ParsePath(true); + } + + private void ReplaceNumbersWith(string replacement) + { + var fileName = Path.GetFileName(SpritePath); + var path = Path.GetDirectoryName(SpritePath); + fileName = fileName.Replace(replacement, c => char.IsNumber(c)); + SpritePath = Path.Combine(path, fileName); + } + + private void ParsePath(bool parseSpritePath) + { + if (_gender != Gender.None) + { + SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male"); + } + SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString()); + if (!File.Exists(SpritePath)) + { + // If the variant does not exist, parse the path so that it uses first variant. + Variant = 1; + ReplaceNumbersWith(Variant.ToString()); + } + if (parseSpritePath) + { + Sprite.ParseTexturePath(file: SpritePath); + } + } + public bool IsInitialized { get; private set; } public void Init(Gender gender = Gender.None) { if (IsInitialized) { return; } _gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None; - if (_gender != Gender.None) - { - SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male"); - } + ParsePath(false); if (Sprite != null) { Sprite.Remove(); } - Sprite = new Sprite(SourceElement, "", SpritePath); + Sprite = new Sprite(SourceElement, file: SpritePath); Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true); HideLimb = SourceElement.GetAttributeBool("hidelimb", false); HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false); @@ -170,7 +208,7 @@ namespace Barotrauma.Items.Components get { return damageModifiers; } } - public Wearable (Item item, XElement element) : base(item, element) + public Wearable(Item item, XElement element) : base(item, element) { this.item = item; @@ -197,7 +235,7 @@ namespace Barotrauma.Items.Components limbType[i] = (LimbType)Enum.Parse(typeof(LimbType), subElement.GetAttributeString("limb", "Head"), true); - wearableSprites[i] = new WearableSprite(subElement, this); + wearableSprites[i] = new WearableSprite(subElement, this, variant); foreach (XElement lightElement in subElement.Elements()) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index b8e7c8019..2a1b62490 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -801,7 +801,12 @@ namespace Barotrauma if (findNewHull) FindHull(); } - partial void SetActiveSprite(); + public void SetActiveSprite() + { + SetActiveSpriteProjSpecific(); + } + + partial void SetActiveSpriteProjSpecific(); public override void Move(Vector2 amount) { @@ -1432,13 +1437,13 @@ namespace Barotrauma selectHit = picker.IsKeyHit(ic.SelectKey); #if CLIENT - //if the cursor is on a UI component, disable interaction with the left mouse button - //to prevent accidentally selecting items when clicking UI elements - if (picker == Character.Controlled && GUI.MouseOn != null) - { - if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false; - if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false; - } + //if the cursor is on a UI component, disable interaction with the left mouse button + //to prevent accidentally selecting items when clicking UI elements + if (picker == Character.Controlled && GUI.MouseOn != null) + { + if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false; + if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false; + } #endif } } @@ -1608,6 +1613,7 @@ namespace Barotrauma if (remove) { Spawner?.AddToRemoveQueue(this); } } + List texts = new List(); public List GetHUDTexts(Character character) { texts.Clear(); @@ -1617,6 +1623,13 @@ namespace Barotrauma if (!ic.CanBePicked && !ic.CanBeSelected) continue; if (ic is Holdable holdable && !holdable.CanBeDeattached()) continue; + Color color = Color.Gray; + bool hasRequiredSkillsAndItems = ic.HasRequiredSkills(character) && ic.HasRequiredItems(character, false); + if (hasRequiredSkillsAndItems) + { + color = Color.Cyan; + } + texts.Add(new ColoredText(ic.DisplayMsg, color, false)); } @@ -2054,6 +2067,8 @@ namespace Barotrauma public virtual void Reset() { SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement); + Sprite.ReloadXML(); + SpriteDepth = Sprite.Depth; components.ForEach(c => c.Reset()); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs index 8109974be..213beec73 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs @@ -621,10 +621,25 @@ namespace Barotrauma string treatmentIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant(); - var matchingAffliction = AfflictionPrefab.List.Find(a => a.Identifier == treatmentIdentifier); - if (matchingAffliction != null) + List matchingAfflictions = AfflictionPrefab.List.FindAll(a => a.Identifier == treatmentIdentifier || a.AfflictionType == treatmentIdentifier); + if (matchingAfflictions.Count == 0) { - matchingAffliction.TreatmentSuitability.Add(identifier, subElement.GetAttributeFloat("suitability", 0.0f)); + DebugConsole.ThrowError("Error in item prefab \"" + Name + "\" - couldn't define as a treatment, no treatments with the identifier or type \"" + treatmentIdentifier + "\" were found."); + continue; + } + + float suitability = subElement.GetAttributeFloat("suitability", 0.0f); + foreach (AfflictionPrefab matchingAffliction in matchingAfflictions) + { + if (matchingAffliction.TreatmentSuitability.ContainsKey(identifier)) + { + matchingAffliction.TreatmentSuitability[identifier] = + Math.Max(matchingAffliction.TreatmentSuitability[identifier], suitability); + } + else + { + matchingAffliction.TreatmentSuitability.Add(identifier, suitability); + } } break; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs b/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs index c2f13f46d..bbbfc6705 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/RelatedItem.cs @@ -16,6 +16,8 @@ namespace Barotrauma Container } + public bool IsOptional { get; set; } + private string[] identifiers; private string[] excludedIdentifiers; @@ -138,7 +140,8 @@ namespace Barotrauma { element.Add( new XAttribute("identifiers", JoinedIdentifiers), - new XAttribute("type", type.ToString())); + new XAttribute("type", type.ToString()), + new XAttribute("optional", IsOptional)); if (excludedIdentifiers.Length > 0) { diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs index 798b322b6..3cfbaa599 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs @@ -90,6 +90,25 @@ namespace Barotrauma } } + public string DisplayName + { + get; + private set; + } + + private string roomName; + [Editable, Serialize("", true, translationTextTag: "RoomName.")] + public string RoomName + { + get { return roomName; } + set + { + if (roomName == value) { return; } + roomName = value; + DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName; + } + } + public override Rectangle Rect { get @@ -817,17 +836,17 @@ namespace Barotrauma } if (roomItems.Contains("reactor")) - return TextManager.Get("ReactorRoom"); + return "RoomName.ReactorRoom"; else if (roomItems.Contains("engine")) - return TextManager.Get("EngineRoom"); + return "RoomName.EngineRoom"; else if (roomItems.Contains("steering") && roomItems.Contains("sonar")) - return TextManager.Get("CommandRoom"); + return "RoomName.CommandRoom"; else if (roomItems.Contains("ballast")) - return TextManager.Get("Ballast"); + return "RoomName.Ballast"; if (ConnectedGaps.Any(g => !g.IsRoomToRoom && g.ConnectedDoor != null)) { - return TextManager.Get("Airlock"); + return "RoomName.Airlock"; } Rectangle subRect = Submarine.CalculateDimensions(); @@ -847,7 +866,7 @@ namespace Barotrauma else roomPos |= Alignment.Right; - return TextManager.Get("Sub" + roomPos.ToString()); + return "RoomName.Sub" + roomPos.ToString(); } public static Hull Load(XElement element, Submarine submarine) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index 4ae545ab1..3f0889562 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -144,6 +144,20 @@ namespace Barotrauma get { return spriteColor; } set { spriteColor = value; } } + + [Editable, Serialize(false, true)] + public bool UseDropShadow + { + get; + private set; + } + + [Serialize("0,0", true), Editable(ToolTip = "The position of the drop shadow relative to the structure. If set to zero, the shadow is positioned automatically so that it points towards the sub's center of mass.")] + public Vector2 DropShadowOffset + { + get; + private set; + } public override Rectangle Rect { @@ -299,8 +313,8 @@ namespace Barotrauma } } - // Only add ai targets automatically to walls - if (aiTarget == null && HasBody && Tags.Contains("wall")) + // Only add ai targets automatically to submarine/outpost walls + if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null) { aiTarget = new AITarget(this); } @@ -1139,6 +1153,13 @@ namespace Barotrauma if (element.GetAttributeBool("flippedx", false)) s.FlipX(false); if (element.GetAttributeBool("flippedy", false)) s.FlipY(false); SerializableProperty.DeserializeProperties(s, element); + + //structures with a body drop a shadow by default + if (element.Attribute("usedropshadow") == null) + { + s.UseDropShadow = prefab.Body; + } + return s; } diff --git a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs index 06d9576f9..eb101d98b 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/StructurePrefab.cs @@ -194,7 +194,14 @@ namespace Barotrauma break; case "backgroundsprite": sp.BackgroundSprite = new Sprite(subElement, lazyLoad: true); - + if (subElement.Attribute("sourcerect") == null && sp.sprite != null) + { + sp.BackgroundSprite.SourceRect = sp.sprite.SourceRect; + sp.BackgroundSprite.size = sp.sprite.size; + sp.BackgroundSprite.size.X *= sp.sprite.SourceRect.Width; + sp.BackgroundSprite.size.Y *= sp.sprite.SourceRect.Height; + sp.BackgroundSprite.RelativeOrigin = subElement.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f)); + } if (subElement.GetAttributeBool("fliphorizontal", false)) sp.BackgroundSprite.effects = SpriteEffects.FlipHorizontally; if (subElement.GetAttributeBool("flipvertical", false)) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index ee3c527fc..a0fd7465d 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -417,7 +417,10 @@ namespace Barotrauma if (me.Submarine != this) { continue; } if (me is Item item) { - item.Indestructible = true; + if (item.GetComponent() != null) + { + item.Indestructible = true; + } foreach (ItemComponent ic in item.Components) { if (ic is ConnectionPanel connectionPanel) diff --git a/Barotrauma/BarotraumaShared/Source/Networking/OrderChatMessage.cs b/Barotrauma/BarotraumaShared/Source/Networking/OrderChatMessage.cs index 0f8f53bd7..85523d2fa 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/OrderChatMessage.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/OrderChatMessage.cs @@ -20,7 +20,7 @@ namespace Barotrauma.Networking public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender) : this(order, orderOption, - order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption), + order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption), targetEntity, targetCharacter, sender) { } diff --git a/Barotrauma/BarotraumaShared/Source/Screens/GameScreen.cs b/Barotrauma/BarotraumaShared/Source/Screens/GameScreen.cs index 72bec4a5e..e7a5815ad 100644 --- a/Barotrauma/BarotraumaShared/Source/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaShared/Source/Screens/GameScreen.cs @@ -44,6 +44,7 @@ namespace Barotrauma base.Deselect(); #if CLIENT + GameMain.Config.SaveNewPlayerConfig(); GameMain.SoundManager.SetCategoryMuffle("default", false); GUI.ClearMessages(); #endif diff --git a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs index 0921bd91d..064b5d1e2 100644 --- a/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaShared/Source/Sprite/Sprite.cs @@ -87,7 +87,6 @@ namespace Barotrauma public string FullPath { get; private set; } - public override string ToString() { return FilePath + ": " + sourceRect; @@ -107,25 +106,7 @@ namespace Barotrauma { this.lazyLoad = lazyLoad; SourceElement = element; - if (file == "") - { - file = SourceElement.GetAttributeString("texture", ""); - } - if (file == "") - { - DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!"); - return; - } - if (!string.IsNullOrEmpty(path)) - { - LoadTexture(ref sourceVector, ref shouldReturn, preMultipliedAlpha); - } - FilePath = path + file; - if (!string.IsNullOrEmpty(FilePath)) - { - FullPath = Path.GetFullPath(FilePath); - } - + if (!ParseTexturePath(path, file)) { return; } Name = SourceElement.GetAttributeString("name", null); Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero); preMultipliedAlpha = preMultiplyAlpha ?? SourceElement.GetAttributeBool("premultiplyalpha", true); @@ -269,6 +250,29 @@ namespace Barotrauma ID = GetID(SourceElement); } } + + public bool ParseTexturePath(string path = "", string file = "") + { + if (file == "") + { + file = SourceElement.GetAttributeString("texture", ""); + } + if (file == "") + { + DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!"); + return false; + } + if (!string.IsNullOrEmpty(path)) + { + if (!path.EndsWith("/")) path += "/"; + } + FilePath = path + file; + if (!string.IsNullOrEmpty(FilePath)) + { + FullPath = Path.GetFullPath(FilePath); + } + return true; + } } } diff --git a/Barotrauma/BarotraumaShared/Source/TextManager.cs b/Barotrauma/BarotraumaShared/Source/TextManager.cs index 1d5d074c8..52df9ac7b 100644 --- a/Barotrauma/BarotraumaShared/Source/TextManager.cs +++ b/Barotrauma/BarotraumaShared/Source/TextManager.cs @@ -252,6 +252,28 @@ namespace Barotrauma return null; } + public static List> GetAllTagTextPairs() + { + if (!textPacks.ContainsKey(Language)) + { + DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English..."); + Language = "English"; + if (!textPacks.ContainsKey(Language)) + { + throw new Exception("No text packs available in English!"); + } + } + + List> allText = new List>(); + + foreach (TextPack textPack in textPacks[Language]) + { + allText.AddRange(textPack.GetAllTagTextPairs()); + } + + return allText; + } + public static string ReplaceGenderPronouns(string text, Gender gender) { if (gender == Gender.Male) diff --git a/Barotrauma/BarotraumaShared/Source/TextPack.cs b/Barotrauma/BarotraumaShared/Source/TextPack.cs index 91366d043..f1c2c3a76 100644 --- a/Barotrauma/BarotraumaShared/Source/TextPack.cs +++ b/Barotrauma/BarotraumaShared/Source/TextPack.cs @@ -37,6 +37,7 @@ namespace Barotrauma text = text.Replace("&", "&"); text = text.Replace("<", "<"); text = text.Replace(">", ">"); + text = text.Replace(""", "\""); infoList.Add(text); } } @@ -62,6 +63,20 @@ namespace Barotrauma return textList; } + public List> GetAllTagTextPairs() + { + var pairs = new List>(); + foreach (KeyValuePair> kvp in texts) + { + foreach (string line in kvp.Value) + { + pairs.Add(new KeyValuePair(kvp.Key, line)); + } + } + + return pairs; + } + #if DEBUG public void CheckForDuplicates(int index) { diff --git a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub index 94e47fc22..3d7eb5493 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub and b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub index ee8d2d85a..a0fb19aae 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub and b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 511746b0c..6b354c42d 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub index 8b43c675f..c57bc40b9 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub and b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index c20881e5c..4d734b45c 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index 2383ef1a9..0e552098c 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub index 16b9dd843..f546031bf 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub and b/Barotrauma/BarotraumaShared/Submarines/RemoraDrone.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub index 23c9548e5..72e8f6ace 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub and b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub index 1113c5152..14f516834 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Venture.sub b/Barotrauma/BarotraumaShared/Submarines/Venture.sub index d72b72043..e6df2ee70 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Venture.sub and b/Barotrauma/BarotraumaShared/Submarines/Venture.sub differ