From 039d07f693a36032c26b860866f7dafe9abe3f19 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Thu, 16 May 2019 05:04:10 +0300 Subject: [PATCH] (a2943d8b7) Merge branch 'dev' into human-ai --- .../BarotraumaClient/LinuxClient.csproj | 54 +++++ .../Source/Characters/Animation/Ragdoll.cs | 7 +- .../Characters/Health/CharacterHealth.cs | 45 ++-- .../Source/Characters/Limb.cs | 1 + .../BarotraumaClient/Source/DebugConsole.cs | 34 ++- .../Source/Fonts/ScalableFont.cs | 11 + Barotrauma/BarotraumaClient/Source/GUI/GUI.cs | 2 +- .../Source/GUI/GUIComponent.cs | 7 +- .../Source/GUI/GUIMessageBox.cs | 6 + .../BarotraumaClient/Source/GUI/GUIStyle.cs | 85 +------ .../BarotraumaClient/Source/GUI/GUITickBox.cs | 5 +- .../BarotraumaClient/Source/GameMain.cs | 4 + .../Source/GameSession/CrewManager.cs | 105 ++++++--- .../GameModes/MultiPlayerCampaign.cs | 2 + .../Source/Items/CharacterInventory.cs | 152 +++++++++++-- .../Source/Items/Components/ItemComponent.cs | 8 +- .../Source/Items/Components/ItemLabel.cs | 15 +- .../Items/Components/Machines/MiniMap.cs | 2 +- .../Items/Components/Machines/Steering.cs | 7 - .../Components/Signal/CustomInterface.cs | 36 ++- .../Source/Items/Inventory.cs | 21 +- .../BarotraumaClient/Source/Items/Item.cs | 37 ++- .../BarotraumaClient/Source/Map/Hull.cs | 27 +++ .../Source/Networking/ChatMessage.cs | 2 +- .../Source/Networking/SteamManager.cs | 2 + .../Source/Screens/CampaignSetupUI.cs | 2 - .../Source/Screens/CampaignUI.cs | 164 +++++++++++--- .../Source/Screens/CharacterEditorScreen.cs | 213 ++++++++++++------ .../Source/Screens/MainMenuScreen.cs | 8 +- .../Source/Screens/NetLobbyScreen.cs | 31 ++- .../Source/Screens/SubEditorScreen.cs | 2 +- .../Serialization/SerializableEntityEditor.cs | 122 +++++++--- .../Source/Sounds/SoundPlayer.cs | 161 ------------- .../Source/Utils/LocalizationCSVtoXML.cs | 11 +- Barotrauma/BarotraumaServer/Server.csproj | 13 +- .../BarotraumaServer/Source/DebugConsole.cs | 2 +- .../BarotraumaServer/Source/GameMain.cs | 2 +- .../GameModes/MultiPlayerCampaign.cs | 15 ++ .../Data/ContentPackages/Vanilla 0.9.xml | 11 +- .../BarotraumaShared/SharedContent.projitems | 198 ++++++++++++---- .../Source/Characters/AI/EnemyAIController.cs | 14 +- .../Source/Characters/AI/HumanAIController.cs | 4 +- .../Characters/AI/IndoorsSteeringManager.cs | 26 ++- .../AI/Objectives/AIObjectiveCombat.cs | 8 +- .../AI/Objectives/AIObjectiveFindSafety.cs | 21 +- .../AI/Objectives/AIObjectiveIdle.cs | 14 +- .../AI/Objectives/AIObjectiveRescue.cs | 2 +- .../Animation/FishAnimController.cs | 16 -- .../Animation/HumanoidAnimController.cs | 18 +- .../Animation/Params/Ragdoll/RagdollParams.cs | 1 + .../Source/Characters/Animation/Ragdoll.cs | 4 +- .../Source/Characters/Attack.cs | 12 +- .../Source/Characters/Character.cs | 8 +- .../Characters/Health/CharacterHealth.cs | 9 +- .../Source/Extensions/StringFormatter.cs | 16 ++ .../Source/GameSession/CargoManager.cs | 2 +- .../GameSession/GameModes/CampaignMode.cs | 34 +++ .../Source/Items/CharacterInventory.cs | 14 +- .../Source/Items/Components/Door.cs | 5 +- .../Items/Components/Holdable/RepairTool.cs | 4 +- .../Source/Items/Components/ItemComponent.cs | 2 +- .../Items/Components/Machines/Steering.cs | 13 ++ .../Components/Signal/CustomInterface.cs | 16 +- .../Source/Items/Components/Wearable.cs | 54 ++++- .../Source/Items/ItemPrefab.cs | 39 ++-- .../BarotraumaShared/Source/Map/FireSource.cs | 2 + .../BarotraumaShared/Source/Map/Hull.cs | 31 ++- .../BarotraumaShared/Source/Map/Structure.cs | 7 +- .../BarotraumaShared/Source/Map/Submarine.cs | 19 +- .../Source/Networking/OrderChatMessage.cs | 2 +- .../BarotraumaShared/Source/PlayerInput.cs | 15 -- .../BarotraumaShared/Source/Sprite/Sprite.cs | 44 ++-- .../BarotraumaShared/Source/TextManager.cs | 134 ----------- .../BarotraumaShared/Source/TextPack.cs | 15 ++ .../BarotraumaShared/Source/Utils/SaveUtil.cs | 6 + .../BarotraumaShared/Submarines/Dugong.sub | Bin 73437 -> 58969 bytes 76 files changed, 1407 insertions(+), 856 deletions(-) diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index f0fa4331e..f0d80d9ee 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -193,6 +193,60 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index 69530ef26..847902e84 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -415,7 +415,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 }) { @@ -433,6 +433,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 e1b97e425..d9b25edd0 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -1092,8 +1092,8 @@ namespace Barotrauma List lines = new List(); foreach (MapEntityPrefab me in MapEntityPrefab.List) { - lines.Add("" + me.Name + ""); - lines.Add("" + me.Description + ""); + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); } File.WriteAllLines(filePath, lines); })); @@ -1523,7 +1523,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) @@ -1534,6 +1534,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) @@ -1681,7 +1701,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) { @@ -1690,11 +1710,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/Fonts/ScalableFont.cs b/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs index a5ef20967..a64d9b6e1 100644 --- a/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs +++ b/Barotrauma/BarotraumaClient/Source/Fonts/ScalableFont.cs @@ -414,6 +414,17 @@ namespace Barotrauma return retVal; } + public Vector2 MeasureChar(char c) + { + Vector2 retVal = Vector2.Zero; + retVal.Y = baseHeight * 1.8f; + if (texCoords.TryGetValue(c, out GlyphData gd)) + { + retVal.X = gd.advance; + } + return retVal; + } + public void Dispose() { FontList.Remove(this); 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/GUI/GUIComponent.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIComponent.cs index d7fa5f045..61ae5e113 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIComponent.cs @@ -132,6 +132,8 @@ namespace Barotrauma public bool IgnoreLayoutGroups; + public bool IgnoreLayoutGroups; + public virtual ScalableFont Font { get; @@ -513,6 +515,7 @@ namespace Barotrauma yield return CoroutineStatus.Running; } + toolTipBlock.SetTextPos(); SetAlpha(to); @@ -538,11 +541,9 @@ namespace Barotrauma OutlineColor = style.OutlineColor; - public virtual void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, Vector2? flashRectInflate = null) + public virtual void Flash(Color? color = null, float flashDuration = 1.5f) { flashTimer = flashDuration; - this.flashRectInflate = flashRectInflate ?? Vector2.Zero; - this.useRectangleFlash = useRectangleFlash; this.flashDuration = flashDuration; flashColor = (color == null) ? Color.Red : (Color)color; } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIMessageBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIMessageBox.cs index ec9c026e6..435c43066 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIMessageBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIMessageBox.cs @@ -47,6 +47,12 @@ namespace Barotrauma Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Tag = tag; + InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, Anchor.Center) { IsFixedSize = false }, style: null); + GUI.Style.Apply(InnerFrame, "", this); + + Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; + Tag = tag; + if (height == 0) { string wrappedText = ToolBox.WrapText(text, Content.Rect.Width, GUI.Font); diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs index f7006dea9..a3b89fd53 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIStyle.cs @@ -78,85 +78,6 @@ namespace Barotrauma break; } } - return defaultFont; - } - - - private void RescaleFonts() - { - foreach (XElement subElement in configElement.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "font": - Font.Size = GetFontSize(subElement); - break; - case "smallfont": - SmallFont.Size = GetFontSize(subElement); - break; - case "largefont": - LargeFont.Size = GetFontSize(subElement); - break; - case "objectivetitle": - ObjectiveTitleFont.Size = GetFontSize(subElement); - break; - case "objectivename": - ObjectiveNameFont.Size = GetFontSize(subElement); - break; - case "videotitle": - VideoTitleFont.Size = GetFontSize(subElement); - break; - } - } - } - - private ScalableFont LoadFont(XElement element, GraphicsDevice graphicsDevice) - { - string file = GetFontFilePath(element); - uint size = GetFontSize(element); - bool dynamicLoading = GetFontDynamicLoading(element); - return new ScalableFont(file, size, graphicsDevice, dynamicLoading); - } - - private uint GetFontSize(XElement element) - { - foreach (XElement subElement in element.Elements()) - { - if (subElement.Name.ToString().ToLowerInvariant() != "size") { continue; } - Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue)); - if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y) - { - return (uint)subElement.GetAttributeInt("size", 14); - } - } - return 14; - } - - private string GetFontFilePath(XElement element) - { - foreach (XElement subElement in element.Elements()) - { - if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } - string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); - if (GameMain.Config.Language.ToLowerInvariant() == language) - { - return subElement.GetAttributeString("file", ""); - } - } - return element.GetAttributeString("file", ""); - } - - private bool GetFontDynamicLoading(XElement element) - { - foreach (XElement subElement in element.Elements()) - { - if (subElement.Name.ToString().ToLowerInvariant() != "override") { continue; } - string language = subElement.GetAttributeString("language", "").ToLowerInvariant(); - if (GameMain.Config.Language.ToLowerInvariant() == language) - { - return subElement.GetAttributeBool("dynamicloading", false); - } - } return element.GetAttributeBool("dynamicloading", false); } @@ -172,6 +93,12 @@ namespace Barotrauma return style; } + public GUIComponentStyle GetComponentStyle(string name) + { + componentStyles.TryGetValue(name.ToLowerInvariant(), out GUIComponentStyle style); + return style; + } + public void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null) { GUIComponentStyle componentStyle = null; diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs index 913914243..889628213 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITickBox.cs @@ -44,10 +44,11 @@ namespace Barotrauma public Color TextColor { - get { return box; } + get { return text.TextColor; } + set { text.TextColor = value; } } - public GUITextBlock TextBlock + public override Rectangle MouseRect { get { diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index 96409dd29..12f296bf5 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -180,6 +180,10 @@ namespace Barotrauma GUI.KeyboardDispatcher = new EventInput.KeyboardDispatcher(Window); + GUI.KeyboardDispatcher = new EventInput.KeyboardDispatcher(Window); + + + PerformanceCounter = new PerformanceCounter(); PerformanceCounter = new PerformanceCounter(); diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index 9f8baaf79..2a0aa2f37 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -83,6 +83,66 @@ namespace Barotrauma break; } } + + var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); + reportButtonFrame = new GUILayoutGroup(new RectTransform( + new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)) + { + AbsoluteSpacing = (int)(5 * GUI.Scale), + UserData = "reportbuttons", + CanBeFocused = false + }; + + //report buttons + foreach (Order order in reports) + { + if (!order.TargetAllCharacters || order.SymbolSprite == null) continue; + var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null) + { + OnClicked = (GUIButton button, object userData) => + { + if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false; + SetCharacterOrder(null, order, null, Character.Controlled); + HumanAIController.PropagateHullSafety(Character.Controlled, Character.Controlled.CurrentHull); + return true; + }, + UserData = order, + ToolTip = order.Name + }; + + new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") + { + Color = Color.Red * 0.8f, + HoverColor = Color.Red * 1.0f, + PressedColor = Color.Red * 0.6f, + UserData = "highlighted", + CanBeFocused = false, + Visible = false + }; + + var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite, scaleToFit: true) + { + Color = order.Color, + HoverColor = Color.Lerp(order.Color, Color.White, 0.5f), + ToolTip = order.Name + }; + } + + screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); + + prevUIScale = GUI.Scale; + + ToggleCrewAreaOpen = GameMain.Config.CrewMenuOpen; + } + + + #endregion + + #region Character list management + + public Rectangle GetCharacterListArea() + { + return characterListBox.Rect; } partial void InitProjectSpecific() @@ -606,27 +666,20 @@ namespace Barotrauma characterListBox.BarScroll = roundedPos; } - #endregion - - - #region Voice chat - - public void SetPlayerVoiceIconState(Client client, bool muted, bool mutedLocally) + #region Dialog + /// + /// Adds the message to the single player chatbox. + /// + public void AddSinglePlayerChatMessage(string senderName, string text, ChatMessageType messageType, Character sender) { - if (client?.Character == null) { return; } - - var playerFrame = characterListBox.Content.FindChild(client.Character)?.FindChild(client.Character); - if (playerFrame == null) { return; } - var soundIcon = playerFrame.FindChild("soundicon"); - var soundIconDisabled = playerFrame.FindChild("soundicondisabled"); - - if (!soundIcon.Visible) + if (!isSinglePlayer) { - soundIcon.Color = new Color(soundIcon.Color, 0.0f); + DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace); + return; } - soundIcon.Visible = !muted && !mutedLocally; - soundIconDisabled.Visible = muted || mutedLocally; - soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally"); + if (string.IsNullOrEmpty(text)) { return; } + + ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender)); } private IEnumerable KillCharacterAnim(GUIComponent component) @@ -639,6 +692,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); @@ -750,7 +809,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 { @@ -767,7 +826,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) { @@ -841,12 +900,6 @@ namespace Barotrauma matchingItems.RemoveAll(it => it.Submarine != submarine && !submarine.DockedTo.Contains(it.Submarine)); matchingItems.RemoveAll(it => it.Submarine != null && it.Submarine.IsOutpost); } - var characterElement = characterListBox.Content.FindChild(character); - GUIButton orderBtn = characterElement.FindChild(order, recursive: true) as GUIButton; - if (orderBtn.Frame.FlashTimer <= 0) - { - orderBtn.Flash(color, 1.5f, false, flashRectInflate); - } //more than one target item -> create a minimap-like selection with a pic of the sub if (matchingItems.Count > 1) 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/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs index 4be33d829..4b232ad92 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 | InvSlotType.OuterClothes; + 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,7 +438,34 @@ namespace Barotrauma QuickUseItem(Items[i], true, false, true); } } - + } + + public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) + { + if (!AccessibleWhenAlive && !character.IsDead) + { + syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f); + return; + } + + base.Update(deltaTime, cam); + + //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) { @@ -703,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++) { @@ -717,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; } @@ -727,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 68997c299..7462d2b18 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -380,9 +380,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/Steering.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs index 42e370fa7..1b93cd18f 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Steering.cs @@ -577,13 +577,6 @@ namespace Barotrauma.Items.Components } } - protected override void RemoveComponentSpecific() - { - maintainPosIndicator?.Remove(); - maintainPosOriginIndicator?.Remove(); - steeringIndicator?.Remove(); - } - public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null) { msg.Write(autoPilot); 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 6a8918041..119b398ac 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 @@ -579,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) { @@ -614,6 +617,10 @@ namespace Barotrauma } } + ic.CreateEditingHUD(componentEditor); + componentEditor.Recalculate(); + } + PositionEditingHUD(); SetHUDLayout(); @@ -830,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/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs index 6f5fad842..b755074bb 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs @@ -353,6 +353,33 @@ namespace Barotrauma Color.Green, width: 2); } } + + foreach (MapEntity e in linkedTo) + { + if (e is Hull) + { + Hull linkedHull = (Hull)e; + Rectangle connectedHullRect = e.Submarine == null ? + linkedHull.rect : + new Rectangle( + (int)(Submarine.DrawPosition.X + linkedHull.WorldPosition.X), + (int)(Submarine.DrawPosition.Y + linkedHull.WorldPosition.Y), + linkedHull.WorldRect.Width, linkedHull.WorldRect.Height); + + //center of the hull + Rectangle currentHullRect = Submarine == null ? + WorldRect : + new Rectangle( + (int)(Submarine.DrawPosition.X + WorldPosition.X), + (int)(Submarine.DrawPosition.Y + WorldPosition.Y), + WorldRect.Width, WorldRect.Height); + + GUI.DrawLine(spriteBatch, + new Vector2(currentHullRect.X, -currentHullRect.Y), + new Vector2(connectedHullRect.X, -connectedHullRect.Y), + Color.Green, width: 2); + } + } } public static void UpdateVertices(GraphicsDevice graphicsDevice, Camera cam, WaterRenderer renderer) 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/SteamManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs index 5ec4a4699..bcc5d714e 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs @@ -598,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/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs index 9c0f44c52..3ad2a9ac7 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs @@ -20,8 +20,6 @@ namespace Barotrauma private GUILayoutGroup subPreviewContainer; - private GUILayoutGroup subPreviewContainer; - private GUIButton loadGameButton; public Action StartNewGame; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index f09e7c014..d4a0cfdd0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -9,15 +9,11 @@ namespace Barotrauma { class CampaignUI { - public enum Tab { Map, Crew, Store } + public enum Tab { Map, Crew, Store, Repair } private Tab selectedTab; private GUIFrame[] tabs; private GUIFrame topPanel; - private GUIButton startButton; - - private GUIFrame topPanel; - private GUIListBox characterList; private GUIListBox myItemList; @@ -28,6 +24,8 @@ namespace Barotrauma private GUIComponent selectedLocationInfo; private GUIListBox selectedMissionInfo; + private GUIButton repairHullsButton, repairItemsButton; + private GUIFrame characterPreviewFrame; private List tabButtons = new List(); @@ -41,10 +39,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; } @@ -114,7 +109,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 @@ -159,7 +154,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 @@ -220,6 +215,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) @@ -332,8 +418,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; } @@ -351,8 +436,7 @@ namespace Barotrauma { //refresh store view SelectItemCategory(MapEntityCategory.Equipment); - } - + } } private void DrawMap(SpriteBatch spriteBatch, GUICustomComponent mapContainer) @@ -454,7 +538,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, @@ -463,7 +547,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)); } @@ -510,10 +594,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); } @@ -599,8 +683,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)) { @@ -618,8 +701,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)) { @@ -661,6 +743,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) @@ -670,8 +766,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; @@ -709,9 +804,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) @@ -763,11 +857,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; @@ -786,11 +878,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/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index a09059539..614b50e9c 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -635,21 +635,21 @@ namespace Barotrauma " -ownerkey " + ownerKey.ToString(); string filename = "DedicatedServer.exe"; -#if LINUX || OSX +#if LINUX + filename = "./DedicatedServer"; +#elif OSX filename = "mono"; arguments = "./DedicatedServer.exe " + arguments; #endif - var processInfo = new ProcessStartInfo { FileName = filename, - Arguments = arguments, + Arguments = arguments #if !DEBUG WindowStyle = ProcessWindowStyle.Hidden #endif }; GameMain.ServerChildProcess = Process.Start(processInfo); - Thread.Sleep(1000); //wait until the server is ready before connecting GameMain.Client = new GameClient(name, "127.0.0.1:" + port.ToString(),ownerKey); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index a4c6c98f5..e9adfa167 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -312,6 +312,25 @@ namespace Barotrauma return true; }; + playYourself = new GUITickBox(new RectTransform(new Vector2(0.06f, 0.06f), myCharacterFrame.RectTransform) { RelativeOffset = new Vector2(0.05f,0.05f) }, + TextManager.Get("PlayYourself")) + { + Selected = true, + OnSelected = TogglePlayYourself, + UserData = "playyourself" + }; + + var toggleMyPlayerFrame = new GUIButton(new RectTransform(new Point(25, 70), myCharacterFrame.RectTransform, Anchor.TopLeft, Pivot.TopRight), "", style: "GUIButtonHorizontalArrow"); + toggleMyPlayerFrame.OnClicked += (GUIButton btn, object userdata) => + { + MyCharacterFrameOpen = !MyCharacterFrameOpen; + foreach (GUIComponent child in btn.Children) + { + child.SpriteEffects = MyCharacterFrameOpen ? SpriteEffects.FlipHorizontally : SpriteEffects.None; + } + return true; + }; + playYourself = new GUITickBox(new RectTransform(new Vector2(0.06f, 0.06f), myCharacterFrame.RectTransform) { RelativeOffset = new Vector2(0.05f,0.05f) }, TextManager.Get("PlayYourself")) { @@ -745,11 +764,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/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index cbce18229..ea5e58cc2 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -1025,7 +1025,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 a2335e8e8..aca4de939 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/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)}" + $" - + PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + - \ No newline at end of file + diff --git a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs index 1dd05bae8..a7d06ffa1 100644 --- a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs @@ -193,7 +193,7 @@ namespace Barotrauma Console.CursorLeft = 0; Console.CursorTop -= cursorLine; Console.Write(input); - Console.CursorLeft = input.Length % Console.WindowWidth; + Console.CursorLeft = input.Length % consoleWidth; } private static void AssignOnClientRequestExecute(string names, Action onClientRequestExecute) 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/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 bd71d9c68..e95896c42 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -24,6 +24,10 @@ + + + + @@ -360,6 +364,78 @@ 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 @@ -594,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 @@ -1304,7 +1380,7 @@ PreserveNewest - + PreserveNewest @@ -1571,7 +1647,7 @@ PreserveNewest - + PreserveNewest @@ -1583,10 +1659,7 @@ PreserveNewest - - PreserveNewest - - + PreserveNewest @@ -1595,10 +1668,10 @@ PreserveNewest - + PreserveNewest - + PreserveNewest @@ -1913,6 +1986,24 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -1931,6 +2022,27 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -2467,18 +2579,6 @@ 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 9cc8fc1ff..364acaaae 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -14,6 +14,11 @@ namespace Barotrauma { public static bool DisableEnemyAI; + /// + /// Enable the character to attack the outposts and the characters inside them. Disabled by default. + /// + public bool TargetOutposts; + class WallTarget { public Vector2 Position; @@ -1043,6 +1048,8 @@ namespace Barotrauma private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && 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 @@ -1068,9 +1075,10 @@ namespace Barotrauma continue; } if (target.Type == AITarget.TargetType.HumanOnly) { continue; } - // Don't attack outposts. - if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; } - + if (!TargetOutposts) + { + if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; } + } Character targetCharacter = target.Entity as Character; //ignore the aitarget if it is the Character itself if (targetCharacter == character) continue; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index e759548fa..2237d4a0b 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -290,7 +290,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); } } } @@ -309,7 +309,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 794dd8443..2d3de2875 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; + } } } } @@ -408,20 +420,18 @@ namespace Barotrauma //door closed and the character can't open doors -> node can't be traversed if (!canOpenDoors || character.LockHands) { 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; + if (!doorButtons.Any()) + { + if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { 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/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 128992a3e..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) { @@ -277,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/FishAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs index 6ccd779c1..2839536b5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs @@ -717,22 +717,6 @@ namespace Barotrauma limb?.body.SmoothRotate(angle, torque, wrapAngle: false); } - private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque) - { - //make sure the angle "has the same number of revolutions" as the reference limb - //(e.g. we don't want to rotate the legs to 0 if the torso is at 360, because that'd blow up the hip joints) - while (referenceLimb.Rotation - angle > MathHelper.TwoPi) - { - angle += MathHelper.TwoPi; - } - while (referenceLimb.Rotation - angle < -MathHelper.TwoPi) - { - angle -= MathHelper.TwoPi; - } - - limb?.body.SmoothRotate(angle, torque, wrapAngle: false); - } - public override void Flip() { base.Flip(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index 138cc61d7..da531aed0 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -534,7 +534,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) { @@ -564,17 +569,16 @@ 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; if (limpAmount > 0.0f) { //make the footpos oscillate when limping - footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f)); + footMid += ((float)Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * 0.3f); } movement = overrideTargetMovement == Vector2.Zero ? @@ -589,7 +593,7 @@ namespace Barotrauma movement.Y = 0.0f; if (torso == null) { return; } - + bool isNotRemote = true; if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) isNotRemote = !character.IsRemotePlayer; @@ -693,7 +697,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 934e0bd3a..3d57328e5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -701,7 +701,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) { @@ -730,7 +730,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/Attack.cs b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs index 0e84aa580..8e0c79593 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs @@ -80,13 +80,13 @@ namespace Barotrauma public HitDetection HitDetectionType { get; private set; } [Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")] - public AIBehaviorAfterAttack AfterAttack { get; private set; } + public AIBehaviorAfterAttack AfterAttack { get; set; } [Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")] public bool Reverse { get; private set; } [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")] - public float Range { get; private set; } + public float Range { get; set; } [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target to do damage. In distance based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired.")] public float DamageRange { get; set; } @@ -95,19 +95,19 @@ namespace Barotrauma public float Duration { get; private set; } [Serialize(5f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How long the AI waits between the attacks.")] - public float CoolDown { get; private set; } = 5; + public float CoolDown { get; set; } = 5; [Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")] - public float SecondaryCoolDown { get; private set; } = 0; + public float SecondaryCoolDown { get; set; } = 0; [Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")] public float CoolDownRandomFactor { get; private set; } = 0; [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)] - public float StructureDamage { get; private set; } + public float StructureDamage { get; set; } [Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)] - public float ItemDamage { get; private set; } + public float ItemDamage { get; set; } /// /// Legacy support. Use Afflictions. diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 720372de4..7257b4c18 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; @@ -2598,6 +2598,10 @@ namespace Barotrauma GameMain.GameSession?.CrewManager?.RemoveCharacter(this); #endif +#if CLIENT + GameMain.GameSession?.CrewManager?.RemoveCharacter(this); +#endif + #if CLIENT GameMain.GameSession?.CrewManager?.RemoveCharacter(this); #endif diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/CharacterHealth.cs index 9a25fcd52..8a2aa9ca9 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. @@ -611,6 +612,12 @@ namespace Barotrauma partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime); + public void SetVitality(float newVitality) + { + maxVitality = newVitality; + CalculateVitality(); + } + public void CalculateVitality() { Vitality = MaxVitality; 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/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs index 12434a1be..4b8d00255 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/CharacterInventory.cs @@ -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 8e632707b..973b7e655 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs @@ -223,11 +223,11 @@ namespace Barotrauma.Items.Components var idCard = character.Inventory.FindItemByIdentifier("idcard"); hasValidIdCard = requiredItems.Any(ri => ri.Value.Any(r => r.MatchesItem(idCard))); - Msg = hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar"; + Msg = requiredItems.None() || hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar"; ParseMsg(); if (addMessage) { - msg = msg ?? (requiredItems.Any(ri => ri.Value.Any(r => r.Identifiers.Contains("idcard"))) ? accessDeniedTxt : cannotOpenText); + 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. @@ -237,6 +237,7 @@ namespace Barotrauma.Items.Components public override bool Pick(Character picker) { if (item.Condition <= RepairThreshold) { return true; } + if (requiredItems.None()) { return false; } if (HasRequiredItems(picker, false) && hasValidIdCard) { return false; } return base.Pick(picker); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index 36e56362a..38953e28a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -338,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 6f0ace085..831c07c39 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; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs index c090837a7..c59d6bce6 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs @@ -185,6 +185,19 @@ namespace Barotrauma.Items.Components return true; } + public override void OnItemLoaded() + { + sonar = item.GetComponent(); + } + + public override bool Select(Character character) + { + if (!CanBeSelected) return false; + + user = character; + return true; + } + public override void Update(float deltaTime, Camera cam) { networkUpdateTimer -= deltaTime; 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/ItemPrefab.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs index e6ff07bc4..0c5ee8062 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs @@ -144,18 +144,6 @@ namespace Barotrauma private List fabricationRecipeElements = new List(); - private bool canSpriteFlipX, canSpriteFlipY; - - private Dictionary prices; - - /// - /// Defines areas where the item can be interacted with. If RequireBodyInsideTrigger is set to true, the character - /// has to be within the trigger to interact. If it's set to false, having the cursor within the trigger is enough. - /// - public List Triggers; - - private List fabricationRecipeElements = new List(); - public string ConfigFile { get { return configFile; } @@ -477,6 +465,12 @@ namespace Barotrauma Tags = element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true).ToHashSet(); } + Tags = new HashSet(element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true)); + if (Tags.None()) + { + Tags = new HashSet(element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true)); + } + if (element.Attribute("cargocontainername") != null) { DebugConsole.ThrowError("Error in item prefab \"" + name + "\" - cargo container should be configured using the item's identifier, not the name."); @@ -636,10 +630,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/Map/FireSource.cs b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs index b87ca0149..0b833e2dd 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/FireSource.cs @@ -25,6 +25,8 @@ namespace Barotrauma private bool removed; + private bool removed; + #if CLIENT private List burnDecals = new List(); #endif 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 c7704d343..55608b242 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -314,8 +314,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); } @@ -1203,6 +1203,9 @@ namespace Barotrauma if (FlippedX) element.Add(new XAttribute("flippedx", true)); if (FlippedY) element.Add(new XAttribute("flippedy", true)); + if (FlippedX) element.Add(new XAttribute("flippedx", true)); + if (FlippedY) element.Add(new XAttribute("flippedy", true)); + for (int i = 0; i < Sections.Length; i++) { if (Sections[i].damage == 0.0f) continue; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index ada123764..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) @@ -533,20 +536,6 @@ namespace Barotrauma { maxX = Math.Min(maxX, ruin.Area.X - 100.0f); } - else - { - maxX = Math.Min(maxX, ruin.Area.X - 100.0f); - } - } - - if (minX < 0.0f && maxX > Level.Loaded.Size.X) - { - //no walls found at either side, just use the initial spawnpos and hope for the best - } - else if (minX < 0) - { - //no wall found at the left side, spawn to the left from the right-side wall - spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset; } if (minX < 0.0f && maxX > Level.Loaded.Size.X) 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/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index 8ad826eea..e13028523 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -162,21 +162,6 @@ namespace Barotrauma get { return binding; } } - public void SetState() - { - hit = binding.IsHit(); - if (hit) hitQueue = true; - - held = binding.IsDown(); - if (held) heldQueue = true; - } -#endif - - public KeyOrMouse State - { - get { return binding; } - } - public void SetState() { hit = binding.IsHit(); 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 b7a01e523..52df9ac7b 100644 --- a/Barotrauma/BarotraumaShared/Source/TextManager.cs +++ b/Barotrauma/BarotraumaShared/Source/TextManager.cs @@ -252,140 +252,6 @@ namespace Barotrauma return null; } - public static string GetFormatted(string textTag, bool returnNull = false, params object[] args) - { - string text = Get(textTag, returnNull); - - if (text == null || text.Length == 0) - { - if (returnNull) - { - return null; - } - else - { - DebugConsole.ThrowError("Text \"" + textTag + "\" not found."); - return textTag; - } - } - - return string.Format(text, args); - } - - // Format: ServerMessage.Identifier1/ServerMessage.Indentifier2~[variable1]=value~[variable2]=value - public static string GetServerMessage(string serverMessage) - { - 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!"); - } - } - - string[] messages = serverMessage.Split('/'); - - try - { - for (int i = 0; i < messages.Length; i++) - { - if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate - { - if (messages[i].Contains(" ")) continue; // Spaces found, do not translate - string msg = Get(messages[i], true); - if (msg != null) // If a translation was found, otherwise use the original - { - messages[i] = msg; - } - } - else - { - string[] messageWithVariables = messages[i].Split('~'); - string msg = Get(messageWithVariables[0], true); - - if (msg != null) // If a translation was found, otherwise use the original - { - messages[i] = msg; - } - else - { - continue; // No translation found, probably caused by player input -> skip variable handling - } - - // First index is always the message identifier -> start at 1 - for (int j = 1; j < messageWithVariables.Length; j++) - { - string[] variableAndValue = messageWithVariables[j].Split('='); - messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]); - } - } - } - - string translatedServerMessage = string.Empty; - for (int i = 0; i < messages.Length; i++) - { - translatedServerMessage += messages[i]; - } - return translatedServerMessage; - } - - catch (IndexOutOfRangeException exception) - { - string errorMsg = "Failed to translate server message \"" + serverMessage + "\"."; -#if DEBUG - DebugConsole.ThrowError(errorMsg, exception); -#endif - GameAnalyticsManager.AddErrorEventOnce("TextManager.GetServerMessage:" + serverMessage, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); - return errorMsg; - } - } - - public static bool IsServerMessageWithVariables(string message) - { - for (int i = 0; i < serverMessageCharacters.Length; i++) - { - if (!message.Contains(serverMessageCharacters[i])) return false; - } - - return true; - } - - public static List GetAll(string textTag) - { - 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; - - foreach (TextPack textPack in textPacks[Language]) - { - allText = textPack.GetAll(textTag); - if (allText != null) return allText; - } - - //if text was not found and we're using a language other than English, see if we can find an English version - //may happen, for example, if a user has selected another language and using mods that haven't been translated to that language - if (Language != "English" && textPacks.ContainsKey("English")) - { - foreach (TextPack textPack in textPacks["English"]) - { - allText = textPack.GetAll(textTag); - if (allText != null) return allText; - } - } - - return null; - } - public static List> GetAllTagTextPairs() { if (!textPacks.ContainsKey(Language)) 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/Source/Utils/SaveUtil.cs b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs index e608871a6..8ae39aa56 100644 --- a/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs +++ b/Barotrauma/BarotraumaShared/Source/Utils/SaveUtil.cs @@ -228,6 +228,12 @@ namespace Barotrauma if (fileName.Length == 0) fileName = "Save"; } + if (fileName == "Save_Default") + { + fileName = TextManager.Get("SaveFile.DefaultName", true); + if (fileName.Length == 0) fileName = "Save"; + } + if (!Directory.Exists(folder)) { DebugConsole.ThrowError("Save folder \"" + folder + "\" not found. Created new folder"); diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 2cef11b71522a3808ac64d6d343bc17bfa73dbd0..6b354c42ddff5df129ec36f2b95538df18877375 100644 GIT binary patch literal 58969 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 Deo7we literal 73437 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

goA+3LxJaErg^-1@dziKWUk1$gV4-;)z=$$ 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%0DGxY#o&g-F;tgLN^T7=nb^@hc?nhRDe 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 F2mp1DA^`vZ