diff --git a/Barotrauma/BarotraumaClient/ClientSource/Camera.cs b/Barotrauma/BarotraumaClient/ClientSource/Camera.cs index b38f63525..4bef949b7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Camera.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Camera.cs @@ -257,7 +257,7 @@ namespace Barotrauma /// public bool Freeze { get; set; } - public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true) + public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true, Rectangle? overrideMouseOn = null) { prevPosition = position; prevZoom = zoom; @@ -294,7 +294,7 @@ namespace Barotrauma } } - if (allowZoom && GUI.MouseOn == null) + if (allowZoom && (GUI.MouseOn == null || (overrideMouseOn?.Contains(PlayerInput.MousePosition) ?? false))) { Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition); Vector2 diffViewCenter; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs index 2427989ad..66c981347 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterInfo.cs @@ -529,6 +529,7 @@ namespace Barotrauma { ushort infoID = inc.ReadUInt16(); string newName = inc.ReadString(); + string originalName = inc.ReadString(); int gender = inc.ReadByte(); int race = inc.ReadByte(); int headSpriteID = inc.ReadByte(); @@ -556,7 +557,7 @@ namespace Barotrauma } // TODO: animations - CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile, variant) + CharacterInfo ch = new CharacterInfo(speciesName, newName, originalName, jobPrefab, ragdollFile, variant) { ID = infoID, }; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs index 2a0b43bb6..2e134561a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Health/CharacterHealth.cs @@ -240,6 +240,8 @@ namespace Barotrauma Character.Controlled.SelectedConstruction = null; } } + + HintManager.OnShowHealthInterface(); } } @@ -718,6 +720,7 @@ namespace Barotrauma int dmgPerSecond = Math.Sign(a2.DamagePerSecond - a1.DamagePerSecond); return dmgPerSecond != 0 ? dmgPerSecond : Math.Sign(a1.Strength - a1.Strength); }); + HintManager.OnAfflictionDisplayed(Character, currentDisplayedAfflictions.FirstOrDefault()); updateDisplayedAfflictionsTimer = UpdateDisplayedAfflictionsInterval; } @@ -1188,7 +1191,7 @@ namespace Barotrauma } } - private Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction) + public static Color GetAfflictionIconColor(AfflictionPrefab prefab, Affliction affliction) { // No specific colors, use generic if (prefab.IconColors == null) @@ -1208,6 +1211,8 @@ namespace Barotrauma } } + public static Color GetAfflictionIconColor(Affliction affliction) => GetAfflictionIconColor(affliction.Prefab, affliction); + private void UpdateAfflictionContainer(LimbHealth selectedLimb) { selectedLimbText.Text = selectedLimb == null ? "" : selectedLimb.Name; @@ -1275,7 +1280,7 @@ namespace Barotrauma var afflictionIcon = new GUIImage(new RectTransform(Vector2.One * 0.8f, button.RectTransform, Anchor.Center), affliction.Prefab.Icon, scaleToFit: true) { - Color = GetAfflictionIconColor(affliction.Prefab, affliction), + Color = GetAfflictionIconColor(affliction), CanBeFocused = false }; afflictionIcon.PressedColor = afflictionIcon.Color; @@ -1911,7 +1916,7 @@ namespace Barotrauma float alpha = MathHelper.Lerp(0.3f, 1.0f, (affliction.Strength - showIconThreshold) / Math.Min(affliction.Prefab.MaxStrength - showIconThreshold, 10.0f)); - affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction.Prefab, affliction) * alpha, 0, iconScale); + affliction.Prefab.Icon.Draw(spriteBatch, iconPos - iconSize / 2.0f, GetAfflictionIconColor(affliction) * alpha, 0, iconScale); iconPos += new Vector2(10.0f, 20.0f) * iconScale; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs index 7252476c8..e914344f7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs @@ -1534,7 +1534,9 @@ namespace Barotrauma List lines = missingTags.Select(t => "\"" + t.Key + "\"\n missing from " + string.Join(", ", t.Value)).ToList(); string filePath = "missingloca.txt"; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(filePath, lines); + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); TextManager.Language = "English"; })); @@ -1543,7 +1545,9 @@ namespace Barotrauma { var debugLines = EventSet.GetDebugStatistics(); string filePath = "eventstats.txt"; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(filePath, debugLines); + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); })); @@ -1824,7 +1828,9 @@ namespace Barotrauma lines.Add("" + me.Name + ""); lines.Add("" + me.Description + ""); } + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(filePath, lines); + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; })); commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) => @@ -1843,16 +1849,15 @@ namespace Barotrauma docs.Add(eventPrefab.ConfigElement.Document); getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier); } + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(filePath, lines); - ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, NewLineOnAttributes = false - }; - + }; foreach (XDocument doc in docs) { using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings)) @@ -1861,6 +1866,7 @@ namespace Barotrauma writer.Flush(); } } + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; void getTextsFromElement(XElement element, List list, string parentName) { @@ -2007,7 +2013,9 @@ namespace Barotrauma lines.Add("[/table]"); lines.Add(""); } + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(filePath, lines); + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; ToolBox.OpenFileWithShell(Path.GetFullPath(filePath)); })); #if DEBUG @@ -2049,7 +2057,9 @@ namespace Barotrauma commands.Add(new Command("printproperties", "Goes through the currently collected property list for missing localizations and writes them to a file.", (string[] args) => { string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\propertylocalization.txt"; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; File.WriteAllLines(path, SerializableEntityEditor.MissingLocalizations); + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; })); commands.Add(new Command("getproperties", "Goes through the MapEntity prefabs and checks their serializable properties for localization issues.", (string[] args) => diff --git a/Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs index 31c5b8913..2d569af49 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs @@ -560,6 +560,22 @@ namespace Barotrauma }; } break; + case NetworkEventType.UNLOCKPATH: + UInt16 connectionIndex = msg.ReadUInt16(); + if (GameMain.GameSession?.Map?.Connections != null) + { + if (connectionIndex >= GameMain.GameSession.Map.Connections.Count) + { + DebugConsole.ThrowError($"Failed to unlock a path on the campaign map. Connection index out of bounds (index: {connectionIndex}, number of connections: {GameMain.GameSession.Map.Connections.Count})"); + } + else + { + GameMain.GameSession.Map.Connections[connectionIndex].Locked = false; + new GUIMessageBox(string.Empty, TextManager.Get("pathunlockedgeneric"), + new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128)); + } + } + break; } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/AbandonedOutpostMission.cs b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/AbandonedOutpostMission.cs index de730f0fc..2f00ca991 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/AbandonedOutpostMission.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/AbandonedOutpostMission.cs @@ -10,7 +10,16 @@ namespace Barotrauma for (int i = 0; i < characterCount; i++) { - characters.Add(Character.ReadSpawnData(msg)); + Character character = Character.ReadSpawnData(msg); + characters.Add(character); + if (msg.ReadBoolean()) { requireKill.Add(character); } + if (msg.ReadBoolean()) + { + requireRescue.Add(character); +#if CLIENT + GameMain.GameSession.CrewManager.AddCharacterToCrewList(character); +#endif + } ushort itemCount = msg.ReadUInt16(); for (int j = 0; j < itemCount; j++) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/Mission.cs b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/Mission.cs index ccdcb04da..37e1f23c8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/Mission.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/Mission.cs @@ -23,7 +23,7 @@ namespace Barotrauma { yield return new WaitForSeconds(1.0f); } - new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon) + new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon, parseRichText: true) { IconColor = Prefab.IconColor }; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/MissionMode.cs b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/MissionMode.cs index 2428d90e2..342b61004 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/MissionMode.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Events/Missions/MissionMode.cs @@ -6,7 +6,7 @@ { foreach (Mission mission in missions) { - new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon) + new GUIMessageBox(mission.Name, mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true) { IconColor = mission.Prefab.IconColor, UserData = "missionstartmessage" diff --git a/Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs b/Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs index f6dad566f..b1e93b11c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs @@ -422,12 +422,12 @@ namespace Barotrauma } } - public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List richTextData) + public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List richTextData, int rtdOffset = 0) { - DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData); + DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, richTextData, rtdOffset); } - public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List richTextData) + public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List richTextData, int rtdOffset = 0) { if (textures.Count == 0 && !DynamicLoading) { return; } @@ -457,13 +457,13 @@ namespace Barotrauma Color currentTextColor; - if (currentRichTextData != null && i > currentRichTextData.EndIndex + lineNum) + while (currentRichTextData != null && i + rtdOffset > currentRichTextData.EndIndex + lineNum) { richTextDataIndex++; currentRichTextData = richTextDataIndex < richTextData.Count ? richTextData[richTextDataIndex] : null; } - if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i && i <= currentRichTextData.EndIndex + lineNum) + if (currentRichTextData != null && currentRichTextData.StartIndex + lineNum <= i + rtdOffset && i + rtdOffset <= currentRichTextData.EndIndex + lineNum) { currentTextColor = currentRichTextData.Color ?? color; if (!string.IsNullOrEmpty(currentRichTextData.Metadata)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs index 41ac0aeba..cd27bae71 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs @@ -321,6 +321,10 @@ namespace Barotrauma float prevSize = chatBox.BarSize; string displayedText = message.TranslatedText; + if (message.Type == ChatMessageType.Server) + { + RichTextData.GetRichTextData(displayedText, out displayedText); + } string senderName = ""; Color senderColor = Color.White; if (!string.IsNullOrWhiteSpace(message.SenderName)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs index a4d1aae70..9e975d413 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs @@ -1,10 +1,10 @@ using Barotrauma.Extensions; +using Barotrauma.Networking; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using Barotrauma.Networking; namespace Barotrauma { @@ -189,7 +189,7 @@ namespace Barotrauma }; new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont); - crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, (8)* height), pendingAndCrewGroup.RectTransform)) + crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform)) { Spacing = 1 }; @@ -208,7 +208,7 @@ namespace Barotrauma { ClickSound = GUISoundType.HireRepairClick, ForceUpperCase = true, - OnClicked = (b, o) => ValidatePendingHires(true) + OnClicked = (b, o) => ValidateHires(PendingHires, true) }; clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall")) { @@ -338,7 +338,7 @@ namespace Barotrauma GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 55)), parent: listBox.Content.RectTransform), "ListBoxElement") { - UserData = new Tuple(characterInfo, skill != null ? skill.Level : 0.0f) + UserData = new Tuple(characterInfo, skill?.Level ?? 0.0f) }; GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft) { @@ -346,19 +346,22 @@ namespace Barotrauma }; float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width; - new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform), + var icon = new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform), onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2())) { CanBeFocused = false }; - GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)); - GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), - characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft) + GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform)) { CanBeFocused = false }; + GUILayoutGroup nameGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { CanBeFocused = false }; + GUITextBlock nameBlock = new GUITextBlock(new RectTransform(Vector2.One, nameGroup.RectTransform), + listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name, + textColor: jobColor, textAlignment: Alignment.BottomLeft) { CanBeFocused = false }; nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width); + GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform), characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft) { @@ -384,11 +387,18 @@ namespace Barotrauma if (listBox != crewList) { - new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center) + new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), + FormatCurrency(characterInfo.Salary), + textAlignment: Alignment.Center) { CanBeFocused = false }; } + else + { + // Just a bit of padding to make list layouts similar + new GUIFrame(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), style: null) { CanBeFocused = false }; + } if (listBox == hireableList) { @@ -447,6 +457,23 @@ namespace Barotrauma } }; } + + if (listBox == pendingList || listBox == crewList) + { + nameBlock.RectTransform.Resize(new Point(nameBlock.Rect.Width - nameBlock.Rect.Height, nameBlock.Rect.Height)); + nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width); + nameBlock.RectTransform.Resize(new Point((int)(nameBlock.Padding.X + nameBlock.TextSize.X + nameBlock.Padding.Z), nameBlock.Rect.Height)); + Point size = new Point((int)(0.7f * nameBlock.Rect.Height)); + new GUIImage(new RectTransform(size, nameGroup.RectTransform), "EditIcon") { CanBeFocused = false }; + size = new Point(3 * mainGroup.AbsoluteSpacing + icon.Rect.Width + nameAndJobGroup.Rect.Width, mainGroup.Rect.Height); + new GUIButton(new RectTransform(size, frame.RectTransform) { RelativeOffset = new Vector2(0.025f) }, style: null) + { + Enabled = HasPermission, + ToolTip = TextManager.GetWithVariable("campaigncrew.givenicknametooltip", "[mouseprimary]", TextManager.Get($"input.{(PlayerInput.MouseButtonsSwapped() ? "rightmouse" : "leftmouse")}")), + UserData = characterInfo, + OnClicked = CreateRenamingComponent + }; + } } private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo) @@ -479,7 +506,10 @@ namespace Barotrauma GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true }; float blockHeight = 1.0f / 4; new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name")); - new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), characterInfo.Name); + GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), ""); + string name = listBox == hireableList ? characterInfo.OriginalName : characterInfo.Name; + nameBlock.Text = ToolBox.LimitString(name, nameBlock.Font, nameBlock.Rect.Width); + if (characterInfo.HasGenders) { new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender")); @@ -554,9 +584,18 @@ namespace Barotrauma if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); } pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple).Item1 == characterInfo)); pendingList.UpdateScrollBarSize(); - CreateCharacterFrame(characterInfo, hireableList); - SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData); - hireableList.UpdateScrollBarSize(); + + // Server will reset the names to originals in multiplayer + if (!GameMain.IsMultiplayer) { characterInfo?.ResetName(); } + + if (campaign.Map.CurrentLocation.HireManager.AvailableCharacters.Any(info => info.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName()) && + hireableList.Content.Children.None(c => c.UserData is Tuple userData && userData.Item1.GetIdentifierUsingOriginalName() == characterInfo.GetIdentifierUsingOriginalName())) + { + CreateCharacterFrame(characterInfo, hireableList); + SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData); + hireableList.UpdateScrollBarSize(); + } + if (setTotalHireCost) { SetTotalHireCost(); } if (createNetworkMessage) { SendCrewState(true); } return true; @@ -573,36 +612,41 @@ namespace Barotrauma { if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; } int total = 0; - pendingList.Content.Children.ForEach(c => total += (c.UserData as Tuple).Item1.Salary); + pendingList.Content.Children.ForEach(c => + { + total += (c.UserData as Tuple).Item1.Salary; + }); totalBlock.Text = FormatCurrency(total); bool enoughMoney = campaign != null ? total <= campaign.Money : true; totalBlock.TextColor = enoughMoney ? Color.White : Color.Red; validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any(); } - public bool ValidatePendingHires(bool createNetworkEvent = false) + public bool ValidateHires(List hires, bool createNetworkEvent = false) { - List hires = new List(); - int total = 0; - foreach (GUIComponent c in pendingList.Content.Children.ToList()) - { - if (c.UserData is Tuple info) - { - hires.Add(info.Item1); - total += info.Item1.Salary; - } - } + if (hires == null || hires.None()) { return false; } - if (hires.None() || total > campaign.Money) { return false; } + List nonDuplicateHires = new List(); + hires.ForEach(hireInfo => + { + if(campaign.CrewManager.GetCharacterInfos().None(crewInfo => crewInfo.IsNewHire && crewInfo.GetIdentifierUsingOriginalName() == hireInfo.GetIdentifierUsingOriginalName())) + { + nonDuplicateHires.Add(hireInfo); + } + }); + + if (nonDuplicateHires.None()) { return false; } + + int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary); + + if (total > campaign.Money) { return false; } bool atLeastOneHired = false; - foreach (CharacterInfo ci in hires) + foreach (CharacterInfo ci in nonDuplicateHires) { if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci)) { atLeastOneHired = true; - PendingHires.Remove(ci); - pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple).Item1 == ci)); } else { @@ -629,6 +673,93 @@ namespace Barotrauma return false; } + private bool CreateRenamingComponent(GUIButton button, object userData) + { + if (!HasPermission || !(userData is CharacterInfo characterInfo)) { return false; } + var outerGlowFrame = new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center), + style: "OuterGlow", color: Color.Black * 0.7f); + var frame = new GUIFrame(new RectTransform(new Vector2(0.33f, 0.4f), outerGlowFrame.RectTransform, anchor: Anchor.Center) + { + MaxSize = new Point(400, 300).Multiply(GUI.Scale) + }); + var layoutGroup = new GUILayoutGroup(new RectTransform((frame.Rect.Size - GUIStyle.ItemFrameMargin).Multiply(new Vector2(0.75f, 1.0f)), frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter) + { + RelativeSpacing = 0.02f, + Stretch = true + }; + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), layoutGroup.RectTransform), TextManager.Get("campaigncrew.givenickname"), font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true); + var groupElementSize = new Vector2(1.0f, 0.25f); + var nameBox = new GUITextBox(new RectTransform(groupElementSize, layoutGroup.RectTransform)) + { + MaxTextLength = Client.MaxNameLength + }; + new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("confirm")) + { + OnClicked = (button, userData) => + { + if (RenameCharacter(characterInfo, nameBox.Text?.Trim())) + { + parentComponent.RemoveChild(outerGlowFrame); + return true; + } + else + { + nameBox.Flash(color: Color.Red); + return false; + } + + } + }; + new GUIButton(new RectTransform(groupElementSize, layoutGroup.RectTransform), text: TextManager.Get("cancel")) + { + OnClicked = (button, userData) => + { + parentComponent.RemoveChild(outerGlowFrame); + return true; + } + }; + layoutGroup.Recalculate(); + return true; + } + + public bool RenameCharacter(CharacterInfo characterInfo, string newName) + { + if (characterInfo == null || string.IsNullOrEmpty(newName)) { return false; } + if (newName == characterInfo.Name) { return false; } + if (GameMain.IsMultiplayer) + { + SendCrewState(false, renameCharacter: (characterInfo, newName)); + } + else + { + var crewComponent = crewList.Content.FindChild(c => (c.UserData as Tuple).Item1 == characterInfo); + if (crewComponent != null) + { + crewList.Content.RemoveChild(crewComponent); + campaign.CrewManager.RenameCharacter(characterInfo, newName); + CreateCharacterFrame(characterInfo, crewList); + SortCharacters(crewList, SortingMethod.JobAsc); + } + else + { + var pendingComponent = pendingList.Content.FindChild(c => (c.UserData as Tuple).Item1 == characterInfo); + if (pendingComponent != null) + { + pendingList.Content.RemoveChild(pendingComponent); + campaign.Map.CurrentLocation.HireManager.RenameCharacter(characterInfo, newName); + CreateCharacterFrame(characterInfo, pendingList); + SortCharacters(pendingList, SortingMethod.JobAsc); + SetTotalHireCost(); + } + else + { + return false; + } + } + } + return true; + } + private bool FireCharacter(GUIButton button, object selection) { if (!(selection is CharacterInfo characterInfo)) { return false; } @@ -649,9 +780,10 @@ namespace Barotrauma UpdateLocationView(campaign.Map.CurrentLocation, false); } - if ((GUI.MouseOn?.UserData as Tuple)?.Item1 is CharacterInfo characterInfo) + (GUIComponent highlightedFrame, CharacterInfo highlightedInfo) = FindHighlightedCharacter(GUI.MouseOn); + if (highlightedFrame != null && highlightedInfo != null) { - if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData) + if (characterPreviewFrame == null || highlightedInfo != characterPreviewFrame.UserData) { GUIComponent component = GUI.MouseOn; GUIListBox listBox = null; @@ -674,7 +806,7 @@ namespace Barotrauma if (listBox != null) { - SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo); + SelectCharacter(listBox, highlightedFrame as GUIFrame, highlightedInfo); } } else @@ -688,6 +820,27 @@ namespace Barotrauma characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame); characterPreviewFrame = null; } + + static (GUIComponent, CharacterInfo) FindHighlightedCharacter(GUIComponent c) + { + if (c == null) + { + return default; + } + if (c.UserData is Tuple highlightedData) + { + return (c, highlightedData.Item1); + } + if (c.Parent != null) + { + if (c.Parent is GUIListBox) + { + return default; + } + return FindHighlightedCharacter(c.Parent); + } + return default; + } } public void SetPendingHires(List characterInfos, Location location) @@ -700,7 +853,7 @@ namespace Barotrauma PendingHires.Clear(); foreach (int identifier in characterInfos) { - CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier); + CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifierUsingOriginalName() == identifier); if (match != null) { PendingHires.Add(match); @@ -717,9 +870,10 @@ namespace Barotrauma /// Notify the server of crew changes /// /// When set to true will tell the server to update the pending hires + /// When not null tell the server to rename this character. Item1 is the character to rename, Item2 is the new name, Item3 indicates whether the renamed character is already a part of the crew. /// When not null tell the server to fire this character /// When set to true will tell the server to validate pending hires - public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false) + public void SendCrewState(bool updatePending, (CharacterInfo info, string newName) renameCharacter = default, CharacterInfo firedCharacter = null, bool validateHires = false) { if (campaign is MultiPlayerCampaign) { @@ -732,12 +886,23 @@ namespace Barotrauma msg.Write((ushort)PendingHires.Count); foreach (CharacterInfo pendingHire in PendingHires) { - msg.Write(pendingHire.GetIdentifier()); + msg.Write(pendingHire.GetIdentifierUsingOriginalName()); } } msg.Write(validateHires); + bool validRenaming = renameCharacter.info != null && !string.IsNullOrEmpty(renameCharacter.newName); + msg.Write(validRenaming); + if (validRenaming) + { + int identifier = renameCharacter.info.GetIdentifierUsingOriginalName(); + msg.Write(identifier); + msg.Write(renameCharacter.newName); + bool existingCrewMember = campaign.CrewManager?.GetCharacterInfos().Any(ci => ci.GetIdentifierUsingOriginalName() == identifier) ?? false; + msg.Write(existingCrewMember); + } + msg.Write(firedCharacter != null); if (firedCharacter != null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs index a2321dd30..0e1b429b1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs @@ -1338,9 +1338,9 @@ namespace Barotrauma } } - public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1) + public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1) { - DrawLine(sb, t, start, end, clr, depth, width); + DrawLine(sb, t, start, end, clr, depth, (int)width); } public static void DrawLine(SpriteBatch sb, Sprite sprite, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, int width = 1) @@ -1407,7 +1407,7 @@ namespace Barotrauma font.DrawStringWithColors(sb, text, pos, color, 0.0f, Vector2.Zero, 1f, SpriteEffects.None, depth, richTextData); } - public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) + public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1) { if (size.X < 0) { @@ -1422,7 +1422,7 @@ namespace Barotrauma DrawRectangle(sb, new Rectangle((int)start.X, (int)start.Y, (int)size.X, (int)size.Y), clr, isFilled, depth, thickness); } - public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, int thickness = 1) + public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f, float thickness = 1) { if (isFilled) { @@ -1430,15 +1430,15 @@ namespace Barotrauma } else { - sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - sb.Draw(t, new Rectangle(rect.X + thickness, rect.Y + rect.Height - thickness, rect.Width - thickness * 2, thickness), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - - sb.Draw(t, new Rectangle(rect.X, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); - sb.Draw(t, new Rectangle(rect.X + rect.Width - thickness, rect.Y, thickness, rect.Height), null, clr, 0.0f, Vector2.Zero, SpriteEffects.None, depth); + Rectangle srcRect = new Rectangle(0, 0, 1, 1); + sb.Draw(t, new Vector2(rect.X, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height), SpriteEffects.None, depth); + sb.Draw(t, new Vector2(rect.X + thickness, rect.Y), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth); + sb.Draw(t, new Vector2(rect.X + thickness, rect.Bottom - thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(rect.Width - thickness, thickness), SpriteEffects.None, depth); + sb.Draw(t, new Vector2(rect.Right - thickness, rect.Y + thickness), srcRect, clr, 0.0f, Vector2.Zero, new Vector2(thickness, rect.Height - thickness * 2f), SpriteEffects.None, depth); } } - public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, int thickness = 1) + public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1) { Matrix rotate = Matrix.CreateRotationZ(rotation); @@ -1455,7 +1455,7 @@ namespace Barotrauma DrawLine(sb, bottomLeft, topLeft, clr, depth, thickness); } - public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, int thickness = 1) + public static void DrawRectangle(SpriteBatch sb, Vector2[] corners, Color clr, float depth = 0.0f, float thickness = 1) { if (corners.Length != 4) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs index 31233c164..4a1ddb06e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs @@ -705,6 +705,29 @@ namespace Barotrauma if (!Visible) return; DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData); } + + public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Vector2 pos, List richTextData = null) + { + if (Tutorials.Tutorial.ContentRunning) { return; } + + int width = (int)(400 * GUI.Scale); + int height = (int)(18 * GUI.Scale); + Point padding = new Point((int)(10 * GUI.Scale)); + + if (toolTipBlock == null || (string)toolTipBlock.userData != toolTip) + { + toolTipBlock = new GUITextBlock(new RectTransform(new Point(width, height), null), richTextData, toolTip, font: GUI.SmallFont, wrap: true, style: "GUIToolTip"); + toolTipBlock.RectTransform.NonScaledSize = new Point( + (int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).X + padding.X + toolTipBlock.Padding.X + toolTipBlock.Padding.Z), + (int)(GUI.SmallFont.MeasureString(toolTipBlock.WrappedText).Y + padding.Y + toolTipBlock.Padding.Y + toolTipBlock.Padding.W)); + toolTipBlock.userData = toolTip; + } + + toolTipBlock.RectTransform.AbsoluteOffset = pos.ToPoint(); + toolTipBlock.SetTextPos(); + + toolTipBlock.DrawManually(spriteBatch); + } public static void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle targetElement, List richTextData = null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs index 3ff6f35ed..968060b30 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIMessageBox.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; using Barotrauma.Networking; +using Barotrauma.Extensions; namespace Barotrauma { @@ -22,11 +23,11 @@ namespace Barotrauma { Default, InGame, - Vote + Vote, + Hint } public List Buttons { get; private set; } = new List(); - //public GUIFrame BackgroundFrame { get; private set; } public GUILayoutGroup Content { get; private set; } public GUIFrame InnerFrame { get; private set; } public GUITextBlock Header { get; private set; } @@ -58,8 +59,6 @@ namespace Barotrauma public bool AutoClose; - private readonly bool alwaysVisible; - private float openState; private float iconState; private bool iconSwitching; @@ -77,10 +76,17 @@ namespace Barotrauma this.Buttons[0].OnClicked = Close; } - public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null) + public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, bool parseRichText = false) : base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox") { - int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0; + int width = (int)(DefaultWidth * type switch + { + Type.Default => 1.0f, + Type.Hint => 1.25f, + _ => 1.5f + }); + int height = 0; + if (relativeSize.HasValue) { width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X); @@ -107,6 +113,7 @@ namespace Barotrauma Anchor anchor = type switch { Type.InGame => Anchor.TopCenter, + Type.Hint => Anchor.TopRight, Type.Vote => Anchor.TopRight, _ => Anchor.Center }; @@ -127,13 +134,13 @@ namespace Barotrauma Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 }; Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), - headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true); + headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true, parseRichText: parseRichText); GUI.Style.Apply(Header, "", this); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); if (!string.IsNullOrWhiteSpace(text)) { - Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); + Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText); GUI.Style.Apply(Text, "", this); Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = new Point(Text.Rect.Width, Text.Rect.Height); @@ -180,7 +187,6 @@ namespace Barotrauma else if (type == Type.InGame) { InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight); - alwaysVisible = true; CanBeFocused = false; AutoClose = true; GUI.Style.Apply(InnerFrame, "", this); @@ -235,13 +241,13 @@ namespace Barotrauma }; } - Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true); + Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, parseRichText: parseRichText); GUI.Style.Apply(Header, "", this); Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); if (!string.IsNullOrWhiteSpace(text)) { - Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); + Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true, parseRichText: parseRichText); GUI.Style.Apply(Text, "", this); Content.Recalculate(); Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = @@ -266,30 +272,204 @@ namespace Barotrauma } Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y); } - + else if (type == Type.Hint) + { + CanBeFocused = false; + GUI.Style.Apply(InnerFrame, "", this); + + Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f); + var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter) + { + AbsoluteSpacing = absoluteSpacing.Y, + Stretch = true + }; + + var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform), + isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + Stretch = true, + RelativeSpacing = 0.02f + }; + + int iconMaxHeight = 0; + if (icon != null) + { + Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true); + iconMaxHeight = (int)Icon.Sprite.size.Y; + } + else + { + bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle); + Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), + iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true); + if (!iconStyleDefined) + { + Icon.Color = Color.Orange; + } + iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40); + } + + iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight); + int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight); + Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight); + Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight); + + Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform)) + { + AbsoluteSpacing = absoluteSpacing.Y, + }; + + var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null); + + var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft), + isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + Stretch = true, + RelativeSpacing = 0.02f + }; + + var dontShowAgainTickBox = new GUITickBox(new RectTransform(new Vector2(0.5f, 1.0f), tickBoxLayoutGroup.RectTransform), + TextManager.Get("hintmessagebox.dontshowagain")) + { + ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"), + UserData = "dontshowagain" + }; + + //var disableHintsTickBox = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), tickBoxLayoutGroup.RectTransform), + // TextManager.Get("hintmessagebox.disablehints")) + //{ + // ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"), + // UserData = "disablehints" + //}; + + Buttons = new List(1) + { + new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight), + text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall") + { + OnClicked = Close + } + }; + + InputType? closeInput = null; + if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None) + { + closeInput = InputType.Use; + } + else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None) + { + closeInput = InputType.Select; + } + if (closeInput.HasValue) + { + Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("hintmessagebox.dismiss")} ([InputType.{closeInput.Value}])"); + Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) => + { + if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value)) + { + GUIButton btn = component as GUIButton; + btn?.OnClicked(btn, btn.UserData); + btn?.Flash(GUI.Style.Green); + } + }; + } + + Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true); + GUI.Style.Apply(Header, "", this); + Header.RectTransform.MinSize = new Point(0, Header.Rect.Height); + + if (!string.IsNullOrWhiteSpace(text)) + { + Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true); + GUI.Style.Apply(Text, "", this); + Content.Recalculate(); + Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize = + new Point(Text.Rect.Width, Text.Rect.Height); + Text.RectTransform.IsFixedSize = true; + if (string.IsNullOrWhiteSpace(headerText)) + { + Header.RectTransform.Parent = null; + Content.ChildAnchor = Anchor.Center; + } + } + + if (height == 0) + { + height = absoluteSpacing.Y; + int upperContainerHeight = absoluteSpacing.Y; + if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; } + if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; } + upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height); + height += upperContainerHeight; + height += absoluteSpacing.Y; + height += (int)((bottomContainer.RectTransform.RelativeSize.Y / topHorizontalLayoutGroup.RectTransform.RelativeSize.Y) * upperContainerHeight); + height += absoluteSpacing.Y; + if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); } + + InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height); + verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize(); + verticalLayoutGroup.Recalculate(); + topHorizontalLayoutGroup.Recalculate(); + Content.Recalculate(); + tickBoxLayoutGroup.Recalculate(); + } + + InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height); + + Point GetVerticalLayoutGroupSize() + { + return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2); + } + } + MessageBoxes.Add(this); } + /// + /// Use to create a message box of Hint type + /// + public GUIMessageBox(string hintIdentifier, string text, Sprite icon) : this("", text, new string[0], textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon) + { + if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox) + { + dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain; + dontShowAgainTickBox.UserData = hintIdentifier; + } + if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox) + { + disableHintsTickBox.OnSelected = HintManager.OnDisableHints; + disableHintsTickBox.UserData = hintIdentifier; + } + } + + private static Type[] messageBoxTypes; + public static void AddActiveToGUIUpdateList() { - for (int i = 0; i < MessageBoxes.Count; i++) + messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type)); + + foreach (var type in messageBoxTypes) { - if (MessageBoxes[i] is GUIMessageBox alwaysVisibleMsgBox && alwaysVisibleMsgBox.alwaysVisible) + for (int i = 0; i < MessageBoxes.Count; i++) { - alwaysVisibleMsgBox.AddToGUIUpdateList(); - break; - } - } - for (int i = MessageBoxes.Count - 1; i >= 0; i--) - { - if (MessageBoxes[i].UserData as string == "verificationprompt" || - MessageBoxes[i].UserData as string == "bugreporter") - { - continue; - } - if (!(MessageBoxes[i] is GUIMessageBox msgBox) || !msgBox.alwaysVisible) - { - MessageBoxes[i].AddToGUIUpdateList(); + if (MessageBoxes[i] == null) { continue; } + if (!(MessageBoxes[i] is GUIMessageBox messageBox)) + { + if (type == Type.Default) + { + // Message box not of type GUIMessageBox is likely the round summary + MessageBoxes[i].AddToGUIUpdateList(); + break; + } + continue; + } + if (messageBox.type != type) { continue; } + + // These are handled separately in GUI.HandlePersistingElements() + if (MessageBoxes[i].UserData as string == "verificationprompt") { continue; } + if (MessageBoxes[i].UserData as string == "bugreporter") { continue; } + + messageBox.AddToGUIUpdateList(); break; } } @@ -337,11 +517,21 @@ namespace Barotrauma } } - if (type == Type.InGame) + if (type == Type.InGame || type == Type.Hint) { - Vector2 initialPos = new Vector2(0.0f, GameMain.GraphicsHeight); - Vector2 defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale); - Vector2 endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y); + Vector2 initialPos, defaultPos, endPos; + if (type == Type.InGame) + { + initialPos = new Vector2(0.0f, GameMain.GraphicsHeight); + defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale); + endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y); + } + else + { + initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height); + defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.MessageAreaTop.Height + GUI.IntScale(64)); + endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y); + } if (!closing) { @@ -428,7 +618,7 @@ namespace Barotrauma public void Close() { - if (type == Type.InGame) + if (type == Type.InGame || type == Type.Hint) { closing = true; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs index a0a59d81b..940d6dfc1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs @@ -35,6 +35,7 @@ namespace Barotrauma public readonly Sprite[] CursorSprite = new Sprite[7]; public UISprite RadiationSprite { get; private set; } + public SpriteSheet RadiationAnimSpriteSheet { get; private set; } public UISprite UIGlow { get; private set; } public UISprite UIGlowCircular { get; private set; } @@ -214,6 +215,9 @@ namespace Barotrauma case "radiation": RadiationSprite = new UISprite(subElement); break; + case "radiationanimspritesheet": + RadiationAnimSpriteSheet = new SpriteSheet(subElement); + break; case "uiglowcircular": UIGlowCircular = new UISprite(subElement); break; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs index 523dc49b9..d1be053a0 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs @@ -279,7 +279,8 @@ namespace Barotrauma /// If the rectT height is set 0, the height is calculated from the text. /// public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null, - Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false) + Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, + bool playerInput = false, bool parseRichText = false) : base(style, rectT) { if (color.HasValue) @@ -289,7 +290,13 @@ namespace Barotrauma if (textColor.HasValue) { OverrideTextColor(textColor.Value); - } + } + + if (parseRichText) + { + richTextData = RichTextData.GetRichTextData(text, out text); + hasColorHighlight = richTextData != null; + } //if the text is in chinese/korean/japanese and we're not using a CJK-compatible font, //use the default CJK font as a fallback diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs index ba3c56caf..23b0f9036 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs @@ -70,6 +70,14 @@ namespace Barotrauma } private string selectedTip; + private List selectedTipRichTextData; + private bool selectedTipRichTextUnparsed; + private void SetSelectedTip(string tip) + { + selectedTip = tip; + selectedTipRichTextData = null; + selectedTipRichTextUnparsed = true; + } private readonly object loadMutex = new object(); private float? loadState; @@ -115,7 +123,7 @@ namespace Barotrauma overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png"); noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero); DrawLoadingText = true; - selectedTip = TextManager.Get("LoadingScreenTip", true); + SetSelectedTip(TextManager.Get("LoadingScreenTip", true)); } public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime) @@ -215,14 +223,34 @@ namespace Barotrauma if (GUI.Font != null && selectedTip != null) { + if (selectedTipRichTextUnparsed) + { + selectedTipRichTextData = RichTextData.GetRichTextData(selectedTip, out selectedTip); + selectedTipRichTextUnparsed = false; + } + string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font); string[] lines = wrappedTip.Split('\n'); float lineHeight = GUI.Font.MeasureString(selectedTip).Y; - for (int i = 0; i < lines.Length; i++) + if (selectedTipRichTextData != null) { - GUI.Font.DrawString(spriteBatch, lines[i], - new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White); + int rtdOffset = 0; + for (int i = 0; i < lines.Length; i++) + { + GUI.Font.DrawStringWithColors(spriteBatch, lines[i], + new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White, + 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f, selectedTipRichTextData, rtdOffset); + rtdOffset += lines[i].Length; + } + } + else + { + for (int i = 0; i < lines.Length; i++) + { + GUI.Font.DrawString(spriteBatch, lines[i], + new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White); + } } } @@ -302,7 +330,7 @@ namespace Barotrauma { GameMain.Config.Language = language; //reload tip in the selected language - selectedTip = TextManager.Get("LoadingScreenTip", true); + SetSelectedTip(TextManager.Get("LoadingScreenTip", true)); GameMain.Config.SetDefaultBindings(legacy: false); GameMain.Config.CheckBindings(useDefaults: true); WaitForLanguageSelection = false; @@ -364,7 +392,7 @@ namespace Barotrauma { drawn = false; LoadState = null; - selectedTip = TextManager.Get("LoadingScreenTip", true); + SetSelectedTip(TextManager.Get("LoadingScreenTip", true)); currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture; while (!drawn) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs index 81b438eaa..19a70e9c8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs @@ -1284,7 +1284,8 @@ namespace Barotrauma // Add items on the sub(s) Submarine.MainSub?.GetItems(true) .Where(i => i.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached) && - i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) + i.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null)) && + ItemAndAllContainersInteractable(i)) .ForEach(i => AddToOwnedItems(i.Prefab)); // Add items in character inventories @@ -1302,6 +1303,16 @@ namespace Barotrauma ownedItemsUpdateTimer = 0.0f; + static bool ItemAndAllContainersInteractable(Item item) + { + do + { + if (!item.IsPlayerTeamInteractable) { return false; } + item = item.Container; + } while (item != null); + return true; + } + void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1) { if (OwnedItems.ContainsKey(itemPrefab)) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs index a9b5fc466..8b4d1161d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/TabMenu.cs @@ -882,10 +882,15 @@ namespace Barotrauma GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null); GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft); - string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont); - string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font); string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward)); - string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font); + + var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString); + var missionDescriptionRichTextData = RichTextData.GetRichTextData(mission.Description, out string missionDescriptionString); + var missionRewardRichTextData = RichTextData.GetRichTextData(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), out string missionRewardString); + + missionNameString = ToolBox.WrapText(missionNameString, missionTextGroup.Rect.Width, GUI.LargeFont); + missionDescriptionString = ToolBox.WrapText(missionDescriptionString, missionTextGroup.Rect.Width, GUI.Font); + missionRewardString = ToolBox.WrapText(missionRewardString, missionTextGroup.Rect.Width, GUI.Font); Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString); Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString); @@ -901,12 +906,12 @@ namespace Barotrauma int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio)); Point iconSize = new Point(iconWidth, iconHeight); - new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor, HoverColor = mission.Prefab.IconColor }; + new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor }; } - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString); - } + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameRichTextData, missionNameString, font: GUI.LargeFont); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardRichTextData, missionRewardString); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionRichTextData, missionDescriptionString); + } } else { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs index 90432b668..fe99a061f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameMain.cs @@ -465,7 +465,28 @@ namespace Barotrauma while (Config.WaitingForAutoUpdate) { yield return CoroutineStatus.Running; } } - + +#if DEBUG + if (Config.ModBreakerMode) + { + Config.SelectCorePackage(ContentPackage.CorePackages.GetRandom()); + foreach (var regularPackage in ContentPackage.RegularPackages) + { + if (Rand.Range(0.0, 1.0) <= 0.5) + { + Config.EnableRegularPackage(regularPackage); + } + else + { + Config.DisableRegularPackage(regularPackage); + } + } + ContentPackage.SortContentPackages(p => + { + return Rand.Int(int.MaxValue); + }); + } +#endif if (Config.AllEnabledPackages.None()) { @@ -535,6 +556,7 @@ namespace Barotrauma Order.Init(); EventManagerSettings.Init(); BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature)); + HintManager.Init(); TitleScreen.LoadState = 50.0f; yield return CoroutineStatus.Running; @@ -920,6 +942,8 @@ namespace Barotrauma Client.AddToGUIUpdateList(); } + SubmarinePreview.AddToGUIUpdateList(); + FileSelection.AddToGUIUpdateList(); DebugConsole.AddToGUIUpdateList(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs index a7ca8fedd..9385af1e3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs @@ -330,7 +330,10 @@ namespace Barotrauma if (removeInfo) { characterInfos.Remove(character.Info); } } - private void AddCharacterToCrewList(Character character) + /// + /// Add character to the list without actually adding it to the crew + /// + public void AddCharacterToCrewList(Character character) { if (character == null) { return; } @@ -416,7 +419,8 @@ namespace Barotrauma font: font, textColor: character.Info?.Job?.Prefab?.UIColor) { - CanBeFocused = false + CanBeFocused = false, + UserData = "name" }; nameBlock.Text = ToolBox.LimitString(character.Name, font, (int)nameBlock.Rect.Width); @@ -429,22 +433,7 @@ namespace Barotrauma { UserData = character }; - - // Only create a tooltip if the name doesn't fit the name block - if (nameBlock.Text.EndsWith("...")) - { - var characterTooltip = character.Name; - if (character.Info?.Job?.Name != null) { characterTooltip += " (" + character.Info.Job.Name + ")"; }; - characterButton.ToolTip = characterTooltip; - if (character.Info?.Job?.Prefab != null) - { - characterButton.TooltipRichTextData = new List() { new RichTextData() - { - Color = character.Info.Job.Prefab.UIColor, - EndIndex = characterTooltip.Length - 1 - }}; - } - } + SetCharacterButtonTooltip(characterButton); if (IsSinglePlayer) { @@ -482,6 +471,14 @@ namespace Barotrauma UserData = character }; currentOrderList.RectTransform.IsFixedSize = true; + currentOrderList.OnAddedToGUIUpdateList += (component) => + { + if (component is GUIListBox list) + { + list.CanBeFocused = CanIssueOrders; + list.CanDragElements = CanIssueOrders && list.Content.CountChildren > 1; + } + }; // Previous orders new GUILayoutGroup(new RectTransform(Vector2.One, parent: orderGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) @@ -526,14 +523,26 @@ namespace Barotrauma }; } + private void SetCharacterButtonTooltip(GUIButton characterButton) + { + var character = (Character)characterButton.UserData; + if (character?.Info?.Job?.Prefab == null) { return; } + string color = XMLExtensions.ColorToString(character.Info.Job.Prefab.UIColor); + string tooltip = $"‖color:{color}‖{character.Name} ({character.Info.Job.Name})‖color:end‖"; + var richTextData = RichTextData.GetRichTextData(tooltip, out string sanitizedTooltip); + characterButton.ToolTip = sanitizedTooltip; + characterButton.TooltipRichTextData = richTextData; + } + /// /// Sets which character is selected in the crew UI (highlight effect etc) /// public bool CharacterClicked(GUIComponent component, object selection) { if (!AllowCharacterSwitch) { return false; } - Character character = selection as Character; - if (character == null || character.IsDead || character.IsUnconscious) { return false; } + if (!(selection is Character character) || character.IsDead || character.IsUnconscious) { return false; } + if (!character.IsOnPlayerTeam) { return false; } + SelectCharacter(character); if (GUI.KeyboardDispatcher.Subscriber == crewList) { GUI.KeyboardDispatcher.Subscriber = null; } return true; @@ -588,6 +597,15 @@ namespace Barotrauma yield return CoroutineStatus.Success; } + partial void RenameCharacterProjSpecific(CharacterInfo characterInfo) + { + if (!(crewList.Content.GetChildByUserData(characterInfo?.Character) is GUIComponent characterComponent)) { return; } + if (!(characterComponent.FindChild("name", recursive: true) is GUITextBlock nameBlock)) { return; } + nameBlock.Text = ToolBox.LimitString(characterInfo.Name, nameBlock.Font, nameBlock.Rect.Width); + if (!(characterComponent.FindChild(c => c is GUIButton && c.UserData == characterInfo?.Character) is GUIButton characterButton)) { return; } + SetCharacterButtonTooltip(characterButton); + } + #endregion #region Dialog @@ -835,7 +853,6 @@ namespace Barotrauma if (order == null || order.Identifier == dismissedOrderPrefab.Identifier || updatedExistingIcon) { - currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1; RearrangeIcons(); return; } @@ -874,7 +891,6 @@ namespace Barotrauma nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex); } - currentOrderIconList.CanDragElements = currentOrderIconList.Content.CountChildren > 1; RearrangeIcons(); void RearrangeIcons() @@ -1276,6 +1292,7 @@ namespace Barotrauma } DisableCommandUI(); Character.Controlled = character; + HintManager.OnChangeCharacter(); } private int TryAdjustIndex(int amount) @@ -1537,6 +1554,11 @@ namespace Barotrauma if (characterComponent.UserData is Character character) { characterComponent.Visible = Character.Controlled == null || Character.Controlled.TeamID == character.TeamID; + if (character.TeamID == CharacterTeamType.FriendlyNPC && Character.Controlled != null && + (character.CurrentHull == Character.Controlled.CurrentHull || Vector2.DistanceSquared(Character.Controlled.WorldPosition, character.WorldPosition) < 500.0f * 500.0f)) + { + characterComponent.Visible = true; + } if (characterComponent.Visible) { if (character == Character.Controlled && characterComponent.State != GUIComponent.ComponentState.Selected) @@ -1817,6 +1839,8 @@ namespace Barotrauma { Character.Controlled.dontFollowCursor = true; } + + HintManager.OnShowCommandInterface(); } private void ToggleCommandUI() @@ -3218,14 +3242,14 @@ namespace Barotrauma #endif if (order.Identifier == dismissedOrderPrefab.Identifier) { - return characters.FindAll(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList(); + return characters.Union(GetOrderableFriendlyNPCs()).Where(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList(); } return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList(); } private IEnumerable GetCharactersSortedForOrder(Order order, bool includeSelf) { - return characters.FindAll(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)) + return characters.Where(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)).Union(GetOrderableFriendlyNPCs()) // 1. Prioritize those who are on the same submarine than the controlled character .OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine) // 2. Prioritize those who are already ordered to operate the item target of the new 'operate' order, or given the same maintenance order as now issued @@ -3242,6 +3266,12 @@ namespace Barotrauma .ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill)); } + private IEnumerable GetOrderableFriendlyNPCs() + { + return crewList.Content.Children.Where(c => c.UserData is Character character && character.TeamID == CharacterTeamType.FriendlyNPC).Select(c => (Character)c.UserData); + } + + #endregion #endregion diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs index 0aafbeb48..e9e8c0d08 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs @@ -63,7 +63,7 @@ namespace Barotrauma { new GUIMessageBox( mission.Prefab.IsSideObjective ? TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), mission.Name) : mission.Name, - mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon) + mission.Description, new string[0], type: GUIMessageBox.Type.InGame, icon: mission.Prefab.Icon, parseRichText: true) { IconColor = mission.Prefab.IconColor, UserData = "missionstartmessage" diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs index 2d7d0d0e7..cdeaefec3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -437,8 +437,10 @@ namespace Barotrauma { ShowCampaignUI = false; } + HintManager.OnAvailableTransition(transitionType); } } + public override void End(TransitionType transitionType = TransitionType.None) { base.End(transitionType); @@ -649,7 +651,7 @@ namespace Barotrauma { string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer); - GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed); + GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure, mapSeed); campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode; campaign.CampaignID = campaignID; GameMain.NetLobbyScreen.ToggleCampaignMode(true); @@ -777,16 +779,29 @@ namespace Barotrauma { pendingHires.Add(msg.ReadInt32()); } - - bool validateHires = msg.ReadBoolean(); + + ushort hiredLength = msg.ReadUInt16(); + List hiredCharacters = new List(); + for (int i = 0; i < hiredLength; i++) + { + CharacterInfo hired = CharacterInfo.ClientRead("human", msg); + hired.Salary = msg.ReadInt32(); + hiredCharacters.Add(hired); + } + + bool renameCrewMember = msg.ReadBoolean(); + if (renameCrewMember) + { + int renamedIdentifier = msg.ReadInt32(); + string newName = msg.ReadString(); + CharacterInfo renamedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier); + if (renamedCharacter != null) { CrewManager.RenameCharacter(renamedCharacter, newName); } + } bool fireCharacter = msg.ReadBoolean(); - - int firedIdentifier = -1; - if (fireCharacter) { firedIdentifier = msg.ReadInt32(); } - if (fireCharacter) { + int firedIdentifier = msg.ReadInt32(); CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier); // this one might and is allowed to be null since the character is already fired on the original sender's game if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); } @@ -794,10 +809,10 @@ namespace Barotrauma if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null) { - CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires); - if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); } - CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation); - if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); } + CampaignUI.CrewManagement.SetHireables(map.CurrentLocation, availableHires); + if (hiredCharacters.Any()) { CampaignUI.CrewManagement.ValidateHires(hiredCharacters); } + CampaignUI.CrewManagement.SetPendingHires(pendingHires, map.CurrentLocation); + if (renameCrewMember || fireCharacter) { CampaignUI.CrewManagement.UpdateCrew(); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs index 8b2c4fd35..46de59f2b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs @@ -57,11 +57,12 @@ namespace Barotrauma /// /// Instantiates a new single player campaign /// - private SinglePlayerCampaign(string mapSeed) : base(GameModePreset.SinglePlayerCampaign) + private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign) { CampaignMetadata = new CampaignMetadata(this); UpgradeManager = new UpgradeManager(this); - map = new Map(this, mapSeed); + map = new Map(this, mapSeed, settings); + Settings = settings; foreach (JobPrefab jobPrefab in JobPrefab.Prefabs) { for (int i = 0; i < jobPrefab.InitialCount; i++) @@ -85,11 +86,14 @@ namespace Barotrauma { switch (subElement.Name.ToString().ToLowerInvariant()) { + case "campaignsettings": + Settings = new CampaignSettings(subElement); + break; case "crew": GameMain.GameSession.CrewManager = new CrewManager(subElement, true); break; case "map": - map = Map.Load(this, subElement); + map = Map.Load(this, subElement, Settings); break; case "metadata": CampaignMetadata = new CampaignMetadata(this, subElement); @@ -141,9 +145,9 @@ namespace Barotrauma /// /// Start a completely new single player campaign /// - public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub) + public static SinglePlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings) { - var campaign = new SinglePlayerCampaign(mapSeed); + var campaign = new SinglePlayerCampaign(mapSeed, settings); return campaign; } @@ -608,6 +612,7 @@ namespace Barotrauma { ShowCampaignUI = false; } + HintManager.OnAvailableTransition(transitionType); } if (!crewDead) @@ -629,9 +634,9 @@ namespace Barotrauma if (nextLevel == null) { //no level selected -> force the player to select one + ForceMapUI = true; CampaignUI.SelectTab(InteractionType.Map); map.SelectLocation(-1); - ForceMapUI = true; return false; } else if (transitionType == TransitionType.ProgressToNextEmptyLocation) @@ -704,6 +709,7 @@ namespace Barotrauma new XAttribute("purchasedhullrepairs", PurchasedHullRepairs), new XAttribute("purchaseditemrepairs", PurchasedItemRepairs), new XAttribute("cheatsenabled", CheatsEnabled)); + modeElement.Add(Settings.Save()); //save and remove all items that are in someone's inventory so they don't get included in the sub file as well foreach (Character c in Character.CharacterList) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/CaptainTutorial.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/CaptainTutorial.cs index f757bce5c..f0491a5df 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/CaptainTutorial.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/CaptainTutorial.cs @@ -99,7 +99,7 @@ namespace Barotrauma.Tutorials captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition; tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent(); tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent(); - var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor")); + var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("medicaldoctor")); captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor"); captain_medic.TeamID = CharacterTeamType.Team1; captain_medic.GiveJobItems(null); @@ -122,17 +122,17 @@ namespace Barotrauma.Tutorials SetDoorAccess(tutorial_lockedDoor_1, null, false); SetDoorAccess(tutorial_lockedDoor_2, null, false); - var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic")); + var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic")); captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic"); captain_mechanic.TeamID = CharacterTeamType.Team1; captain_mechanic.GiveJobItems(); - var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); + var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer")); captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer"); captain_security.TeamID = CharacterTeamType.Team1; captain_security.GiveJobItems(); - var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); + var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")); captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer"); captain_engineer.TeamID = CharacterTeamType.Team1; captain_engineer.GiveJobItems(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/DoctorTutorial.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/DoctorTutorial.cs index b918275ac..2dab531d3 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/DoctorTutorial.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/DoctorTutorial.cs @@ -78,7 +78,7 @@ namespace Barotrauma.Tutorials var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull; medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull; - var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); + var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant")); patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1"); patient1.TeamID = CharacterTeamType.Team1; patient1.GiveJobItems(null); @@ -86,26 +86,26 @@ namespace Barotrauma.Tutorials patient1.AddDamage(patient1.WorldPosition, new List() { new Affliction(AfflictionPrefab.Burn, 15.0f) }, stun: 0, playSound: false); patient1.AIController.Enabled = false; - assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant")); + assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("assistant")); patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2"); patient2.TeamID = CharacterTeamType.Team1; patient2.GiveJobItems(null); patient2.CanSpeak = false; patient2.AIController.Enabled = false; - var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); + var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")); var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3"); subPatient1.TeamID = CharacterTeamType.Team1; subPatient1.AddDamage(patient1.WorldPosition, new List() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false); subPatients.Add(subPatient1); - var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer")); + var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer")); var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3"); subPatient2.TeamID = CharacterTeamType.Team1; subPatient2.AddDamage(patient1.WorldPosition, new List() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false); subPatients.Add(subPatient2); - var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")); + var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")); var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3"); subPatient3.TeamID = CharacterTeamType.Team1; subPatient3.AddDamage(patient1.WorldPosition, new List() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/ScenarioTutorial.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/ScenarioTutorial.cs index 221fc5d0a..dd779c17e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/ScenarioTutorial.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/ScenarioTutorial.cs @@ -110,7 +110,7 @@ namespace Barotrauma.Tutorials } CharacterInfo charInfo = configElement.Element("Character") == null ? - new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) : + new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer")) : new CharacterInfo(configElement.Element("Character")); WayPoint wayPoint = GetSpawnPoint(charInfo); diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/Tutorial.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/Tutorial.cs index bcb0f796d..6d880f5ef 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/Tutorial.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/Tutorials/Tutorial.cs @@ -535,15 +535,15 @@ namespace Barotrauma.Tutorials titleBlock.RectTransform.IsFixedSize = true; } - List richTextData = RichTextData.GetRichTextData(text, out text); + List richTextData = RichTextData.GetRichTextData(" " + text, out text); GUITextBlock textBlock; if (richTextData == null) { - textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), " " + text, wrap: true); + textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), text, wrap: true); } else { - textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, " " + text, wrap: true); + textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), richTextData, text, wrap: true); } textBlock.RectTransform.IsFixedSize = true; diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs index 54537e63a..1a8ebf77f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameSession.cs @@ -88,6 +88,8 @@ namespace Barotrauma } } } + + HintManager.Update(); } public void Draw(SpriteBatch spriteBatch) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs index d0a2be894..4abda1bc4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs @@ -336,7 +336,7 @@ namespace Barotrauma { string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward)); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), - TextManager.GetWithVariable("MissionReward", "[reward]", rewardText)); + TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), parseRichText: true); } if (displayedMission != missionsToDisplay.Last()) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs index 0a9b4eca2..8df78be6c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs @@ -20,6 +20,7 @@ namespace Barotrauma Audio, VoiceChat, Controls, + Gameplay, #if DEBUG Debug #endif @@ -532,29 +533,19 @@ namespace Barotrauma UserData = tab }; - float tabWidth = 0.25f; + float tabWidth = 1.0f / tabs.Length; #if DEBUG - tabWidth = 0.2f; - if (tab != Tab.Debug) - { + string buttonText = tab != Tab.Debug ? TextManager.Get("SettingsTab." + tab.ToString()) : "Debug"; +#else + string buttonText = TextManager.Get("SettingsTab." + tab.ToString()); #endif - tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), - TextManager.Get("SettingsTab." + tab.ToString()), style: "GUITabButton") - { - UserData = tab, - OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; } - }; -#if DEBUG - } - else + + tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), style: "GUITabButton") { - tabButtons[(int)tab] = new GUIButton(new RectTransform(new Vector2(tabWidth, 1.0f), tabButtonHolder.RectTransform), "Debug", style: "GUITabButton") - { - UserData = tab, - OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; } - }; - } -#endif + UserData = tab, + OnClicked = (bt, userdata) => { SelectTab((Tab)userdata); return true; } + }; + tabButtons[(int)tab].Text = ToolBox.LimitString(buttonText, tabButtons[(int)tab].Font, (int)(0.75f * tabWidth * tabButtonHolder.Rect.Width)); } new GUIButton(new RectTransform(new Vector2(0.05f, 0.75f), tabButtonHolder.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.2f) }, style: "GUIBugButton") @@ -669,19 +660,6 @@ namespace Barotrauma Selected = TextureCompressionEnabled }; - GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, leftColumn.RectTransform), - TextManager.Get("PauseOnFocusLost")) - { - Selected = PauseOnFocusLost, - ToolTip = TextManager.Get("PauseOnFocusLostToolTip"), - OnSelected = (tickBox) => - { - PauseOnFocusLost = tickBox.Selected; - UnsavedSettings = true; - return true; - } - }; - GUITextBlock particleLimitText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("ParticleLimit"), font: GUI.SubHeadingFont, wrap: true); GUIScrollBar particleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), style: "GUISlider", barSize: 0.1f) @@ -773,56 +751,6 @@ namespace Barotrauma } }; - GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true); - GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), - style: "GUISlider", barSize: 0.1f) - { - UserData = HUDScaleText, - BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale), - OnMoved = (scrollBar, scroll) => - { - HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll); - ChangeSliderText(scrollBar, HUDScale); - OnHUDScaleChanged?.Invoke(); - return true; - }, - Step = 0.02f - }; - HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll); - - GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont); - GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), - style: "GUISlider", barSize: 0.1f) - { - UserData = inventoryScaleText, - BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale), - OnMoved = (scrollBar, scroll) => - { - InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll); - ChangeSliderText(scrollBar, InventoryScale); - return true; - }, - Step = 0.02f - }; - inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll); - - GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont); - GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), rightColumn.RectTransform), - style: "GUISlider", barSize: 0.1f) - { - UserData = textScaleText, - BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale), - OnMoved = (scrollBar, scroll) => - { - TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll); - textScaleDirty = true; - ChangeSliderText(scrollBar, TextScale); - return true; - }, - Step = 0.01f - }; - textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll); - /// Audio tab ---------------------------------------------------------------- var audioContent = new GUILayoutGroup(new RectTransform(new Vector2(0.97f, 0.97f), tabs[(int)Tab.Audio].RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter) @@ -1380,6 +1308,88 @@ namespace Barotrauma GUITextBlock.AutoScaleAndNormalize(defaultBindingsButton.TextBlock, legacyBindingsButton.TextBlock); }; + /// Gameplay tab ------------------------------------------------------------- + var gameplaySettingsGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.46f, 0.95f), tabs[(int)Tab.Gameplay].RectTransform, Anchor.TopLeft) + { RelativeOffset = new Vector2(0.025f, 0.02f) }) + { RelativeSpacing = 0.01f }; + + GUITickBox pauseOnFocusLostBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform), + TextManager.Get("PauseOnFocusLost")) + { + Selected = PauseOnFocusLost, + ToolTip = TextManager.Get("PauseOnFocusLostToolTip"), + OnSelected = (tickBox) => + { + PauseOnFocusLost = tickBox.Selected; + UnsavedSettings = true; + return true; + } + }; + + GUITickBox disableInGameHintsBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform), + TextManager.Get("DisableInGameHints")) + { + Selected = DisableInGameHints, + ToolTip = TextManager.Get("DisableInGameHintsToolTip"), + OnSelected = (tickBox) => + { + DisableInGameHints = tickBox.Selected; + UnsavedSettings = true; + return true; + } + }; + + GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true); + GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), + style: "GUISlider", barSize: 0.1f) + { + UserData = HUDScaleText, + BarScroll = (HUDScale - MinHUDScale) / (MaxHUDScale - MinHUDScale), + OnMoved = (scrollBar, scroll) => + { + HUDScale = MathHelper.Lerp(MinHUDScale, MaxHUDScale, scroll); + ChangeSliderText(scrollBar, HUDScale); + OnHUDScaleChanged?.Invoke(); + return true; + }, + Step = 0.02f + }; + HUDScaleScrollBar.OnMoved(HUDScaleScrollBar, HUDScaleScrollBar.BarScroll); + + GUITextBlock inventoryScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("InventoryScale"), font: GUI.SubHeadingFont); + GUIScrollBar inventoryScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), + style: "GUISlider", barSize: 0.1f) + { + UserData = inventoryScaleText, + BarScroll = (InventoryScale - MinInventoryScale) / (MaxInventoryScale - MinInventoryScale), + OnMoved = (scrollBar, scroll) => + { + InventoryScale = MathHelper.Lerp(MinInventoryScale, MaxInventoryScale, scroll); + ChangeSliderText(scrollBar, InventoryScale); + return true; + }, + Step = 0.02f + }; + inventoryScaleScrollBar.OnMoved(inventoryScaleScrollBar, inventoryScaleScrollBar.BarScroll); + + GUITextBlock textScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("TextScale"), font: GUI.SubHeadingFont); + GUIScrollBar textScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), + style: "GUISlider", barSize: 0.1f) + { + UserData = textScaleText, + BarScroll = (TextScale - MinTextScale) / (MaxTextScale - MinTextScale), + OnMoved = (scrollBar, scroll) => + { + TextScale = MathHelper.Lerp(MinTextScale, MaxTextScale, scroll); + textScaleDirty = true; + ChangeSliderText(scrollBar, TextScale); + return true; + }, + Step = 0.01f + }; + textScaleScrollBar.OnMoved(textScaleScrollBar, textScaleScrollBar.BarScroll); + + /// Bottom buttons ------------------------------------------------------------- new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), TextManager.Get("Cancel")) { @@ -1465,55 +1475,54 @@ namespace Barotrauma { RelativeOffset = new Vector2(0.02f, 0.02f) }) { RelativeSpacing = 0.01f }; - var automaticQuickStartTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic quickstart enabled", style: "GUITickBox"); - automaticQuickStartTickBox.Selected = AutomaticQuickStartEnabled; - automaticQuickStartTickBox.ToolTip = "Will the game automatically move on to Quickstart when the game is launched"; - automaticQuickStartTickBox.OnSelected = (tickBox) => + void addDebugTickBox(bool initialValue, Action set, string label, string tooltip) { - AutomaticQuickStartEnabled = tickBox.Selected; - UnsavedSettings = true; - return true; - }; + var tickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), label, style: "GUITickBox"); + tickBox.Selected = initialValue; + tickBox.ToolTip = tooltip; + tickBox.OnSelected = (tickBox) => + { + set(tickBox.Selected); + UnsavedSettings = true; + return true; + }; + } - var automaticCampaignLoadTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic campaign load enabled", style: "GUITickBox"); - automaticCampaignLoadTickBox.Selected = AutomaticCampaignLoadEnabled; - automaticCampaignLoadTickBox.ToolTip = "Will the game automatically load the latest campaign save when the game is launched"; - automaticCampaignLoadTickBox.OnSelected = (tickBox) => - { - AutomaticCampaignLoadEnabled = tickBox.Selected; - UnsavedSettings = true; - return true; - }; + addDebugTickBox( + AutomaticQuickStartEnabled, + (b) => AutomaticQuickStartEnabled = b, + "Automatic quickstart enabled", + "Will the game automatically move on to Quickstart when the game is launched"); - var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox"); - showSplashScreenTickBox.Selected = EnableSplashScreen; - showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched"; - showSplashScreenTickBox.OnSelected = (tickBox) => - { - EnableSplashScreen = tickBox.Selected; - UnsavedSettings = true; - return true; - }; + addDebugTickBox( + AutomaticCampaignLoadEnabled, + (b) => AutomaticCampaignLoadEnabled = b, + "Automatic campaign load enabled", + "Will the game automatically load the latest campaign save when the game is launched"); - var verboseLoggingTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Verbose logging enabled", style: "GUITickBox"); - verboseLoggingTickBox.Selected = VerboseLogging; - verboseLoggingTickBox.ToolTip = "Should verbose logging be used"; - verboseLoggingTickBox.OnSelected = (tickBox) => - { - VerboseLogging = tickBox.Selected; - UnsavedSettings = true; - return true; - }; + addDebugTickBox( + EnableSplashScreen, + (b) => EnableSplashScreen = b, + "Splash screen enabled", + "Are the splash screens shown when the game is launched"); - var textManagerDebugModeTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "TextManager debug mode enabled", style: "GUITickBox"); - textManagerDebugModeTickBox.Selected = TextManagerDebugModeEnabled; - textManagerDebugModeTickBox.ToolTip = "Does the TextManager return the text tags for debug purposes?"; - textManagerDebugModeTickBox.OnSelected = (tickBox) => - { - TextManagerDebugModeEnabled = tickBox.Selected; - UnsavedSettings = true; - return true; - }; + addDebugTickBox( + VerboseLogging, + (b) => VerboseLogging = b, + "Verbose logging enabled", + "Should verbose logging be used"); + + addDebugTickBox( + TextManagerDebugModeEnabled, + (b) => TextManagerDebugModeEnabled = b, + "TextManager debug mode enabled", + "Does the TextManager return the text tags for debug purposes?"); + + addDebugTickBox( + ModBreakerMode, + (b) => ModBreakerMode = b, + "Mod breaker mode enabled", + "Do horrible things when loading mods to see if it breaks?"); #endif UnsavedSettings = false; // Reset unsaved settings to false once the UI has been created diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs index 2a6287997..04f6dda92 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs @@ -639,7 +639,13 @@ namespace Barotrauma foreach (Item doubleClickedItem in doubleClickedItems) { QuickUseItem(doubleClickedItem, true, true, true, quickUseAction, playSound: doubleClickedItem == doubleClickedItems.First()); - if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment || !IsInLimbSlot(doubleClickedItem, InvSlotType.Any)) + //only use one item if we're equipping or using it as a treatment + if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment) + { + break; + } + //if the item was put in a limb slot, only put one item from the stack + if (doubleClickedItem.ParentInventory == this && !IsInLimbSlot(doubleClickedItem, InvSlotType.Any)) { break; } @@ -810,6 +816,8 @@ namespace Barotrauma highlightedSubInventorySlot.Inventory.HideTimer = 0.0f; } } + + HintManager.OnShowSubInventory(slotRef?.Item); } public void AssignQuickUseNumKeys() diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs index 0e1db1b6d..c4a0511ea 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs @@ -39,6 +39,8 @@ namespace Barotrauma.Items.Components private Pair tooltip; + private GUITextBlock requiredTimeBlock; + partial void InitProjSpecific() { CreateGUI(); @@ -522,7 +524,7 @@ namespace Barotrauma.Items.Components AutoScaleHorizontal = true, }; - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime), + requiredTimeBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), ToolBox.SecondsToReadableTime(requiredTime), font: GUI.SmallFont); return true; } @@ -606,6 +608,12 @@ namespace Barotrauma.Items.Components } } + partial void UpdateRequiredTimeProjSpecific() + { + if (requiredTimeBlock == null) { return; } + requiredTimeBlock.Text = ToolBox.SecondsToReadableTime(timeUntilReady > 0.0f ? timeUntilReady : requiredTime); + } + public void ClientWrite(IWriteMessage msg, object[] extraData = null) { int itemIndex = pendingFabricatedItem == null ? -1 : fabricationRecipes.IndexOf(pendingFabricatedItem); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs index 8bc8e0d11..a7ee95e8e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Sonar.cs @@ -1380,6 +1380,7 @@ namespace Barotrauma.Items.Components MathHelper.Clamp(c.Mass * 0.03f, 0.1f, 2.0f)); if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); + HintManager.OnSonarSpottedCharacter(Item, c); } continue; } @@ -1399,6 +1400,7 @@ namespace Barotrauma.Items.Components MathHelper.Clamp(limb.Mass * 0.1f, 0.1f, 2.0f)); if (!passive && !CheckBlipVisibility(blip, transducerPos)) { continue; } sonarBlips.Add(blip); + HintManager.OnSonarSpottedCharacter(Item, c); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs index 622ea1cf9..ea629fc0b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Steering.cs @@ -357,8 +357,8 @@ namespace Barotrauma.Items.Components DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false))) { // Undocking from an outpost - campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map); campaign.ShowCampaignUI = true; + campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map); return false; } else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null && @@ -748,7 +748,7 @@ namespace Barotrauma.Items.Components } if (!AutoPilot && Character.DisableControls && GUI.KeyboardDispatcher.Subscriber == null) { - steeringAdjustSpeed = character == null ? 0.2f : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f); + steeringAdjustSpeed = character == null ? DefaultSteeringAdjustSpeed : MathHelper.Lerp(0.2f, 1.0f, character.GetSkillLevel("helm") / 100.0f); Vector2 input = Vector2.Zero; if (PlayerInput.KeyDown(InputType.Left)) { input -= Vector2.UnitX; } if (PlayerInput.KeyDown(InputType.Right)) { input += Vector2.UnitX; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerContainer.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerContainer.cs index 2fd5955c2..4b6c91176 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerContainer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Power/PowerContainer.cs @@ -98,8 +98,11 @@ namespace Barotrauma.Items.Components public override void UpdateHUD(Character character, float deltaTime, Camera cam) { - float chargeRatio = charge / capacity; - chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green); + if (chargeIndicator != null) + { + float chargeRatio = charge / capacity; + chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green); + } } public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Wire.cs index 353e689fe..d1bec4b9f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Signal/Wire.cs @@ -87,7 +87,7 @@ namespace Barotrauma.Items.Components SpriteEffects.None, depth); } - } + } private static Sprite defaultWireSprite; private Sprite overrideSprite; private Sprite wireSprite; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/DockingPort.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/DockingPort.cs index 902e7d3ac..acec84bc8 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/DockingPort.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/DockingPort.cs @@ -167,11 +167,11 @@ namespace Barotrauma.Items.Components { if (DockingTarget.joint != null) { - DockingTarget.Lock(isNetworkMessage: true, forcePosition: true); + DockingTarget.Lock(isNetworkMessage: true); } else { - Lock(isNetworkMessage: true, forcePosition: true); + Lock(isNetworkMessage: true); } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs index 041e145d5..32653c2db 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs @@ -327,10 +327,11 @@ namespace Barotrauma Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom; if (!rect.Contains(pos)) { continue; } - float iconScale = generationParams.LocationIconSize / location.Type.Sprite.size.X; + Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite; + float iconScale = generationParams.LocationIconSize / locationSprite.size.X; if (location == CurrentDisplayLocation) { iconScale *= 1.2f; } - Rectangle drawRect = location.Type.Sprite.SourceRect; + Rectangle drawRect = locationSprite.SourceRect; drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f); drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f); drawRect.X = (int)pos.X - drawRect.Width / 2; @@ -375,13 +376,18 @@ namespace Barotrauma foreach (LocationConnection connection in Connections) { if (HighlightedLocation != CurrentDisplayLocation && - connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation)) + connection.Locations.Contains(HighlightedLocation) && + connection.Locations.Contains(CurrentDisplayLocation)) { if (PlayerInput.PrimaryMouseButtonClicked() && SelectedLocation != HighlightedLocation && HighlightedLocation != null) { + if (connection.Locked) + { + new GUIMessageBox(string.Empty, TextManager.Get("LockedPathTooltip")); + } //clients aren't allowed to select the location without a permission - if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false) + else if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false) { connectionHighlightState = 0.0f; SelectedConnection = connection; @@ -517,8 +523,6 @@ namespace Barotrauma float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1)); - Radiation.Draw(spriteBatch, rect, zoom); - noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(), startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)), color : Color.White * cameraNoiseStrength * 0.1f, @@ -534,6 +538,8 @@ namespace Barotrauma color: Color.White * cameraNoiseStrength * 0.1f, textureScale: Vector2.One * noiseScale); + Radiation.Draw(spriteBatch, rect, zoom); + Pair tooltip = null; if (generationParams.ShowLocations) { @@ -548,8 +554,10 @@ namespace Barotrauma Location location = Locations[i]; if (IsInFogOfWar(location)) { continue; } Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom; - - Rectangle drawRect = location.Type.Sprite.SourceRect; + + Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite; + + Rectangle drawRect = locationSprite.SourceRect; drawRect.X = (int)pos.X - drawRect.Width / 2; drawRect.Y = (int)pos.Y - drawRect.Width / 2; @@ -562,20 +570,14 @@ namespace Barotrauma color *= 0.5f; } - // TODO proper visualization of this - if (location.Type.HasOutpost && !location.HasOutpost()) - { - color = GUI.Style.Red; - } - float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f; if (location == HighlightedLocation) { iconScale *= 1.2f; } - location.Type.Sprite.Draw(spriteBatch, pos, color, - scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom); + locationSprite.Draw(spriteBatch, pos, color, + scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom); if (location == CurrentDisplayLocation) { @@ -657,8 +659,11 @@ namespace Barotrauma DrawDecorativeHUD(spriteBatch, rect); + bool drawRadiationTooltip = true; + if (HighlightedLocation != null) { + drawRadiationTooltip = false; Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom; pos.X += 50 * zoom; Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name); @@ -693,14 +698,23 @@ namespace Barotrauma GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor); } } + if (tooltip != null) { GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First); + drawRadiationTooltip = false; } if (connectionTooltip != null) { GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First); + drawRadiationTooltip = false; } + + if (drawRadiationTooltip) + { + Radiation.DrawFront(spriteBatch); + } + spriteBatch.End(); GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect; spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable); @@ -827,6 +841,7 @@ namespace Barotrauma { if (connection.LevelData.HasBeaconStation) { iconCount++; } if (connection.LevelData.HasHuntingGrounds) { iconCount++; } + if (connection.Locked) { iconCount++; } string tooltip = null; var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth; if (GameMain.GameSession?.Campaign?.UpgradeManager != null) @@ -867,6 +882,11 @@ namespace Barotrauma DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip")); } + if (connection.Locked) + { + DrawIcon("LockIcon", (int)(28 * zoom), TextManager.Get("LockedPathTooltip")); + } + if (connection.LevelData.HasHuntingGrounds) { DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip")); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Radiation.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Radiation.cs index 1fce33720..990d91775 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Radiation.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Radiation.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -6,6 +7,13 @@ namespace Barotrauma { internal partial class Radiation { + private static readonly string radiationTooltip = TextManager.Get("RadiationTooltip"); + private static float spriteIndex; + private readonly SpriteSheet sheet = GUI.Style.RadiationAnimSpriteSheet; + private int maxFrames => sheet.FrameCount + 1; + + private bool isHovingOver; + public void Draw(SpriteBatch spriteBatch, Rectangle container, float zoom) { if (!Enabled) { return; } @@ -19,16 +27,38 @@ namespace Barotrauma Vector2 size = new Vector2((Amount - increasedAmount) * zoom + halfSizeX, viewBottom - topLeft.Y); if (size.X < 0) { return; } - uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, GUI.Style.Red * 0.33f, Vector2.Zero, textureScale: new Vector2(zoom)); + Vector2 spriteScale = new Vector2(zoom); - if (container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X) + uiSprite.Sprite.DrawTiled(spriteBatch, topLeft, size, Params.RadiationAreaColor, Vector2.Zero, textureScale: spriteScale); + + Vector2 topRight = topLeft + Vector2.UnitX * size.X; + + int index = 0; + for (float i = 0; i <= size.Y; i += sheet.FrameSize.Y / 2f * zoom) { - // TODO tooltip? + bool isEven = ++index % 2 == 0; + Vector2 origin = new Vector2(0.5f, 0) * sheet.FrameSize.X; + // every other sprite's animation is reversed to make it seem more chaotic + int sprite = (int) MathF.Floor(isEven ? spriteIndex : maxFrames - spriteIndex); + sheet.Draw(spriteBatch, sprite, topRight + new Vector2(0, i), Params.RadiationBorderTint, origin, 0f, spriteScale); + } + + isHovingOver = container.Contains(PlayerInput.MousePosition) && PlayerInput.MousePosition.X < topLeft.X + size.X; + } + + public void DrawFront(SpriteBatch spriteBatch) + { + if (isHovingOver) + { + GUIComponent.DrawToolTip(spriteBatch, radiationTooltip, PlayerInput.MousePosition + new Vector2(18 * GUI.Scale)); } } public void MapUpdate(float deltaTime) { + float spriteStep = Params.BorderAnimationSpeed * deltaTime; + spriteIndex = (spriteIndex + spriteStep) % maxFrames; + if (increasedAmount > 0) { increasedAmount -= (lastIncrease / Params.AnimationSpeed) * deltaTime; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs index b9cbf543f..ba189ab1f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/MapEntity.cs @@ -805,7 +805,7 @@ namespace Barotrauma } if (selectionPos != null && selectionPos != Vector2.Zero) { - GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f)); + GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, 2f / GameScreen.Selected.Cam.Zoom); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/SubmarineInfo.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/SubmarineInfo.cs index 6ef20f966..050a5337b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/SubmarineInfo.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/SubmarineInfo.cs @@ -35,7 +35,10 @@ namespace Barotrauma public void CreatePreviewWindow(GUIComponent parent) { - var content = new GUIFrame(new RectTransform(Vector2.One, parent.RectTransform), style: null); + var content = new GUIButton(new RectTransform(Vector2.One, parent.RectTransform), style: null) + { + OnClicked = (btn, obj) => { SubmarinePreview.Create(this); return false; } + }; if (PreviewImage == null) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index e0b96af1d..8199a5ede 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -2064,6 +2064,8 @@ namespace Barotrauma.Networking bool autoRestartEnabled = inc.ReadBoolean(); float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f; + bool radiationEnabled = inc.ReadBoolean(); + //ignore the message if we already a more up-to-date one //or if we're still waiting for the initial update if (NetIdUtils.IdMoreRecent(updateID, GameMain.NetLobbyScreen.LastUpdateID) && @@ -2119,7 +2121,7 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.SetAllowSpectating(allowSpectating); GameMain.NetLobbyScreen.LevelSeed = levelSeed; GameMain.NetLobbyScreen.SetLevelDifficulty(levelDifficulty); - GameMain.NetLobbyScreen.SetBotCount(botCount); + GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled); GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode); GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer); @@ -2564,7 +2566,7 @@ namespace Barotrauma.Networking if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaign.CampaignID != campaignID) { string savePath = transfer.FilePath; - GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign); + GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, CampaignSettings.Unsure); campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode; campaign.CampaignID = campaignID; GameMain.NetLobbyScreen.ToggleCampaignMode(true); @@ -2912,7 +2914,7 @@ namespace Barotrauma.Networking clientPeer.Send(msg, DeliveryMethod.Reliable); } - public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed) + public void SetupNewCampaign(SubmarineInfo sub, string saveName, string mapSeed, CampaignSettings settings) { GameMain.NetLobbyScreen.CampaignSetupFrame.Visible = false; GameMain.NetLobbyScreen.CampaignFrame.Visible = false; @@ -2927,6 +2929,7 @@ namespace Barotrauma.Networking msg.Write(mapSeed); msg.Write(sub.Name); msg.Write(sub.MD5Hash.Hash); + settings.Serialize(msg); clientPeer.Send(msg, DeliveryMethod.Reliable); } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/ClientPeer.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/ClientPeer.cs index 3e1a48b58..3f4f5ab1c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/ClientPeer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/ClientPeer.cs @@ -147,13 +147,36 @@ namespace Barotrauma.Networking if (missingPackages.Count > 0) { var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0); + var mismatchedButDownloaded = missingPackages.Where(p => + { + var localMatching = ContentPackage.RegularPackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId); + localMatching ??= ContentPackage.CorePackages.Find(l => l.SteamWorkshopId != 0 && p.WorkshopId == l.SteamWorkshopId); - if (nonDownloadable.Any()) + return localMatching != null; + }); + + if (mismatchedButDownloaded.Any()) + { + string disconnectMsg; + if (mismatchedButDownloaded.Count() == 1) + { + disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMod~[incompatiblecontentpackage]={GetPackageStr(mismatchedButDownloaded.First())}"; + } + else + { + List packageStrs = new List(); + mismatchedButDownloaded.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); + disconnectMsg = $"DisconnectMessage.MismatchedWorkshopMods~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"; + } + Close(disconnectMsg, disableReconnect: true); + OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg); + } + else if (nonDownloadable.Any()) { string disconnectMsg; if (nonDownloadable.Count() == 1) { - disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"; + disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(nonDownloadable.First())}"; } else { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs index cad9bc5ec..76974601b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2PClientPeer.cs @@ -56,6 +56,7 @@ namespace Barotrauma.Networking Steamworks.SteamNetworking.AllowP2PPacketRelay(true); ServerConnection = new SteamP2PConnection("Server", hostSteamId); + ServerConnection.SetOwnerSteamIDIfUnknown(hostSteamId); incomingInitializationMessages = new List(); incomingDataMessages = new List(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2POwnerPeer.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2POwnerPeer.cs index d6f35f5bf..0cbcc298c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2POwnerPeer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/Primitives/Peers/SteamP2POwnerPeer.cs @@ -17,6 +17,7 @@ namespace Barotrauma.Networking class RemotePeer { public UInt64 SteamID; + public UInt64 OwnerSteamID; public double? DisconnectTime; public bool Authenticating; public bool Authenticated; @@ -31,6 +32,7 @@ namespace Barotrauma.Networking public RemotePeer(UInt64 steamId) { SteamID = steamId; + OwnerSteamID = 0; DisconnectTime = null; Authenticating = false; Authenticated = false; @@ -90,10 +92,19 @@ namespace Barotrauma.Networking if (status == Steamworks.AuthResponse.OK) { + remotePeer.OwnerSteamID = ownerID; remotePeer.Authenticated = true; remotePeer.Authenticating = false; foreach (var msg in remotePeer.UnauthedMessages) { + //rewrite the owner id before + //forwarding the messages to + //the server, since it's only + //known now + int prevBitPosition = msg.Message.BitPosition; + msg.Message.BitPosition = sizeof(ulong) * 8; + msg.Message.Write(ownerID); + msg.Message.BitPosition = prevBitPosition; byte[] msgToSend = (byte[])msg.Message.Buffer.Clone(); Array.Resize(ref msgToSend, msg.Message.LengthBytes); ChildServerRelay.Write(msgToSend); @@ -131,6 +142,7 @@ namespace Barotrauma.Networking IWriteMessage outMsg = new WriteOnlyMessage(); outMsg.Write(steamId); + outMsg.Write(remotePeer.OwnerSteamID); outMsg.Write(data, 1, dataLength - 1); DeliveryMethod deliveryMethod = (DeliveryMethod)data[0]; @@ -142,34 +154,27 @@ namespace Barotrauma.Networking bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; - if (!remotePeer.Authenticated) + if (!remotePeer.Authenticated & !remotePeer.Authenticating && isConnectionInitializationStep) { - if (!remotePeer.Authenticating) + remotePeer.DisconnectTime = null; + + IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null); + ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte(); + if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { - if (isConnectionInitializationStep) + remotePeer.Authenticating = true; + + authMsg.ReadString(); //skip name + authMsg.ReadInt32(); //skip owner key + authMsg.ReadUInt64(); //skip steamid + UInt16 ticketLength = authMsg.ReadUInt16(); + byte[] ticket = authMsg.ReadBytes(ticketLength); + + Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId); + if (authSessionStartState != Steamworks.BeginAuthResult.OK) { - remotePeer.DisconnectTime = null; - - IReadMessage authMsg = new ReadOnlyMessage(data, isCompressed, 2, dataLength - 2, null); - ConnectionInitialization initializationStep = (ConnectionInitialization)authMsg.ReadByte(); - //Console.WriteLine("received init step from "+steamId.ToString()+" ("+initializationStep.ToString()+")"); - if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) - { - remotePeer.Authenticating = true; - - authMsg.ReadString(); //skip name - authMsg.ReadInt32(); //skip owner key - authMsg.ReadUInt64(); //skip steamid - UInt16 ticketLength = authMsg.ReadUInt16(); - byte[] ticket = authMsg.ReadBytes(ticketLength); - - Steamworks.BeginAuthResult authSessionStartState = Steam.SteamManager.StartAuthSession(ticket, steamId); - if (authSessionStartState != Steamworks.BeginAuthResult.OK) - { - DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString()); - return; - } - } + DisconnectPeer(remotePeer, DisconnectReason.SteamAuthenticationFailed.ToString() + "/ Steam auth session failed to start: " + authSessionStartState.ToString()); + return; } } } @@ -336,6 +341,7 @@ namespace Barotrauma.Networking { IWriteMessage outMsg = new WriteOnlyMessage(); outMsg.Write(selfSteamID); + outMsg.Write(selfSteamID); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep)); outMsg.Write(Name); @@ -428,6 +434,7 @@ namespace Barotrauma.Networking byte[] msgData = new byte[msg.LengthBytes]; msg.PrepareForSending(ref msgData, out bool isCompressed, out int length); msgToSend.Write(selfSteamID); + msgToSend.Write(selfSteamID); msgToSend.Write((byte)(isCompressed ? PacketHeader.IsCompressed : PacketHeader.None)); msgToSend.Write((UInt16)length); msgToSend.Write(msgData, 0, length); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs index 7474acbb0..0f90dabcb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/ServerSettings.cs @@ -147,7 +147,7 @@ namespace Barotrauma.Networking } } - public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null) + public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null) { if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return; @@ -212,6 +212,7 @@ namespace Barotrauma.Networking outMsg.Write(autoRestart != null); outMsg.Write(autoRestart ?? false); + outMsg.Write(radiationEnabled ?? RadiationEnabled); outMsg.WritePadBits(); } @@ -274,7 +275,7 @@ namespace Barotrauma.Networking if (GUI.MouseOn == btn || GUI.MouseOn == btn.TextBlock) { ToggleSettingsFrame(btn, userData); } return true; }; - + new GUIButton(new RectTransform(Vector2.One, settingsFrame.RectTransform), "", style: null) { OnClicked = ToggleSettingsFrame diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs index 7602d3873..8a154cf79 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignSetupUI.cs @@ -23,7 +23,7 @@ namespace Barotrauma private GUIButton loadGameButton, deleteMpSaveButton; - public Action StartNewGame; + public Action StartNewGame; public Action LoadGame; private enum CategoryFilter { All = 0, Vanilla = 1, Custom = 2 }; @@ -40,6 +40,8 @@ namespace Barotrauma get; private set; } + + public GUITickBox EnableRadiationToggle { get; set; } private readonly bool isMultiplayer; @@ -171,6 +173,8 @@ namespace Barotrauma string savePath = SaveUtil.CreateSavePath(isMultiplayer ? SaveUtil.SaveType.Multiplayer : SaveUtil.SaveType.Singleplayer, saveNameBox.Text); bool hasRequiredContentPackages = selectedSub.RequiredContentPackagesInstalled; + CampaignSettings settings = new CampaignSettings { RadiationEnabled = EnableRadiationToggle?.Selected ?? GameMain.NetLobbyScreen.IsRadiationEnabled() }; + if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages) { if (!hasRequiredContentPackages) @@ -184,7 +188,7 @@ namespace Barotrauma { if (GUIMessageBox.MessageBoxes.Count == 0) { - StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); + StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); if (isMultiplayer) { CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); @@ -204,7 +208,7 @@ namespace Barotrauma msgBox.Buttons[0].OnClicked = (button, obj) => { - StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); + StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); if (isMultiplayer) { CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); @@ -219,7 +223,7 @@ namespace Barotrauma } else { - StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text); + StartNewGame?.Invoke(selectedSub, savePath, seedBox.Text, settings); if (isMultiplayer) { CoroutineManager.StartCoroutine(WaitForCampaignSetup(), "WaitForCampaignSetup"); @@ -230,8 +234,7 @@ namespace Barotrauma } }; - InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1f), buttonContainer.RectTransform), "", - font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green) + InitialMoneyText = new GUITextBlock(new RectTransform(new Vector2(isMultiplayer ? 0.6f : 0.3f, 1f), buttonContainer.RectTransform), "", font: isMultiplayer ? GUI.Style.SmallFont : GUI.Style.Font, textColor: GUI.Style.Green) { TextGetter = () => { @@ -254,6 +257,12 @@ namespace Barotrauma if (!isMultiplayer) { + EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 1f), buttonContainer.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font) + { + Selected = true, + ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip") + }; + var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton") { IgnoreLayoutGroups = true, diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs index d8dc14228..ef57f9d92 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CampaignUI.cs @@ -477,8 +477,8 @@ namespace Barotrauma } string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward)); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), - TextManager.GetWithVariable("missionreward", "[reward]", rewardText), wrap: true); - new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true); + TextManager.GetWithVariable("missionreward", "[reward]", rewardText), wrap: true, parseRichText: true); + new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true); } missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20)); foreach (GUIComponent child in missionTextContent.Children) @@ -562,6 +562,11 @@ namespace Barotrauma public void SelectTab(CampaignMode.InteractionType tab) { + if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map)) + { + HintManager.OnShowCampaignInterface(tab); + } + selectedTab = tab; for (int i = 0; i < tabs.Length; i++) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/CharacterEditorScreen.cs index 1a9426868..49d1430fe 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/CharacterEditor/CharacterEditorScreen.cs @@ -1761,9 +1761,9 @@ namespace Barotrauma.CharacterEditor #endif // Add to the selected content package contentPackage.AddFile(configFilePath, ContentType.Character); - Barotrauma.IO.Validation.DevException = true; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; contentPackage.Save(contentPackage.Path); - Barotrauma.IO.Validation.DevException = false; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; DebugConsole.NewMessage(GetCharacterEditorTranslation("ContentPackageSaved").Replace("[path]", contentPackage.Path)); // Ragdoll diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs index 24b162038..a475dafb4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/EventEditor/EventEditorScreen.cs @@ -882,7 +882,7 @@ namespace Barotrauma return false; } - GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, null); + GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null); TestGameMode gameMode = (TestGameMode) gameSession.GameMode; gameMode.SpawnOutpost = true; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs index ab51ff48f..0f22636f4 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/LevelEditorScreen.cs @@ -296,7 +296,7 @@ namespace Barotrauma subInfo ??= SubmarineInfo.SavedSubmarines.GetRandom(s => s.IsPlayer && !s.HasTag(SubmarineTag.Shuttle) && !nonPlayerFiles.Any(f => f.Path.CleanUpPath().Equals(s.FilePath.CleanUpPath(), StringComparison.InvariantCultureIgnoreCase))); - GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, null); + GameSession gameSession = new GameSession(subInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null); gameSession.StartRound(Level.Loaded.LevelData); (gameSession.GameMode as TestGameMode).OnRoundEnd = () => { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs index a89858069..4f097fad1 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/MainMenuScreen.cs @@ -1043,7 +1043,7 @@ namespace Barotrauma spriteBatch.End(); } - private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed) + private void StartGame(SubmarineInfo selectedSub, string saveName, string mapSeed, CampaignSettings settings) { if (string.IsNullOrEmpty(saveName)) return; @@ -1082,7 +1082,7 @@ namespace Barotrauma selectedSub = new SubmarineInfo(Path.Combine(SaveUtil.TempPath, selectedSub.Name + ".sub")); - GameMain.GameSession = new GameSession(selectedSub, saveName, GameModePreset.SinglePlayerCampaign, mapSeed); + GameMain.GameSession = new GameSession(selectedSub, saveName, GameModePreset.SinglePlayerCampaign, settings, mapSeed); ((SinglePlayerCampaign)GameMain.GameSession.GameMode).LoadNewLevel(); } @@ -1134,6 +1134,7 @@ namespace Barotrauma (int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f), campaignSetupUI.StartButton.RectTransform.MinSize.Y); startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y); + campaignSetupUI.EnableRadiationToggle.RectTransform.Parent = startButtonContainer.RectTransform; campaignSetupUI.InitialMoneyText.RectTransform.Parent = startButtonContainer.RectTransform; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs index c3582ebc4..540674354 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/NetLobbyScreen.cs @@ -27,6 +27,8 @@ namespace Barotrauma private GUIComponent jobVariantTooltip; + private SubmarinePreview submarinePreview; + private readonly GUITextBox chatInput; private readonly GUITextBox serverLogFilter; public GUITextBox ChatInput @@ -41,6 +43,8 @@ namespace Barotrauma private readonly GUIScrollBar levelDifficultyScrollBar; + private readonly GUITickBox radiationEnabledTickBox; + private readonly GUIButton[] traitorProbabilityButtons; private readonly GUITextBlock traitorProbabilityText; @@ -1095,6 +1099,16 @@ namespace Barotrauma } }; + radiationEnabledTickBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font) + { + Selected = true, + OnSelected = box => + { + GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, radiationEnabled: box.Selected); + return true; + } + }; + List settingsElements = settingsContent.Children.ToList(); for (int i = 0; i < settingsElements.Count; i++) { @@ -1238,6 +1252,7 @@ namespace Barotrauma } SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); + radiationEnabledTickBox.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings); botCountButtons[0].Enabled = botCountButtons[1].Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); @@ -2533,8 +2548,14 @@ namespace Barotrauma chatBox.RemoveChild(chatBox.Content.Children.First()); } + string textWithSender = message.TextWithSender; + if (message.Type == ChatMessageType.Server) + { + RichTextData.GetRichTextData(textWithSender, out textWithSender); + } + GUITextBlock msg = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), chatBox.Content.RectTransform), - text: ChatMessage.GetTimeStamp() + (message.Type == ChatMessageType.Private ? TextManager.Get("PrivateMessageTag") + " " : "") + message.TextWithSender, + text: ChatMessage.GetTimeStamp() + (message.Type == ChatMessageType.Private ? TextManager.Get("PrivateMessageTag") + " " : "") + textWithSender, textColor: message.Color, color: ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, wrap: true, font: GUI.SmallFont) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs index f408c64ba..c3eafcebe 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen.cs @@ -43,6 +43,13 @@ namespace Barotrauma private Dictionary pendingWorkshopDownloads = null; private string autoConnectName; private string autoConnectEndpoint; + private enum TernaryOption + { + Any, + Enabled, + Disabled + } + private class FriendInfo { public UInt64 SteamID; @@ -146,16 +153,19 @@ namespace Barotrauma private GUITickBox filterFull; private GUITickBox filterEmpty; private GUITickBox filterWhitelisted; - private GUITickBox filterFriendlyFire; - private GUITickBox filterKarma; - private GUITickBox filterTraitor; - private GUITickBox filterModded; - private GUITickBox filterVoip; + private Dictionary ternaryFilters; private Dictionary filterTickBoxes; private Dictionary playStyleTickBoxes; private Dictionary gameModeTickBoxes; private GUITickBox filterOffensive; + //GUIDropDown sends the OnSelected event before SelectedData is set, so we have to cache it manually. + private TernaryOption filterFriendlyFireValue = TernaryOption.Any; + private TernaryOption filterKarmaValue = TernaryOption.Any; + private TernaryOption filterTraitorValue = TernaryOption.Any; + private TernaryOption filterVoipValue = TernaryOption.Any; + private TernaryOption filterModdedValue = TernaryOption.Any; + private string sortedBy; private GUIButton serverPreviewToggleButton; @@ -173,6 +183,49 @@ namespace Barotrauma CreateUI(); } + private void AddTernaryFilter(RectTransform parent, float elementHeight, string tag, Action valueSetter) + { + var filterLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, elementHeight), parent), isHorizontal: true) + { + Stretch = true + }; + + var box = new GUIFrame(new RectTransform(Vector2.One, filterLayoutGroup.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight) + { + IsFixedSize = true, + }, null) + { + HoverColor = Color.Gray, + SelectedColor = Color.DarkGray, + CanBeFocused = false + }; + if (box.RectTransform.MinSize.Y > 0) + { + box.RectTransform.MinSize = new Point(box.RectTransform.MinSize.Y); + box.RectTransform.Resize(box.RectTransform.MinSize); + } + Vector2 textBlockScale = new Vector2((float)(filterLayoutGroup.Rect.Width - filterLayoutGroup.Rect.Height) / (float)Math.Max(filterLayoutGroup.Rect.Width, 1.0), 1.0f); + + var filterLabel = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f) * textBlockScale, filterLayoutGroup.RectTransform, Anchor.CenterLeft), TextManager.Get("servertag." + tag + ".label"), textAlignment: Alignment.CenterLeft) + { + UserData = TextManager.Get("servertag." + tag + ".label") + }; + GUI.Style.Apply(filterLabel, "GUITextBlock", null); + + var dropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f) * textBlockScale, filterLayoutGroup.RectTransform, Anchor.CenterLeft), elementCount: 3); + dropDown.AddItem(TextManager.Get("any"), TernaryOption.Any); + dropDown.AddItem(TextManager.Get("servertag." + tag + ".true"), TernaryOption.Enabled, TextManager.Get("servertagdescription." + tag + ".true")); + dropDown.AddItem(TextManager.Get("servertag." + tag + ".false"), TernaryOption.Disabled, TextManager.Get("servertagdescription." + tag + ".false")); + dropDown.SelectItem(TernaryOption.Any); + dropDown.OnSelected = (_, data) => { + valueSetter((TernaryOption)data); + FilterServers(); + return true; + }; + + ternaryFilters.Add(tag, dropDown); + } + private void CreateUI() { menu = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.85f), GUI.Canvas, Anchor.Center) { MinSize = new Point(GameMain.GraphicsHeight, 0) }); @@ -323,6 +376,7 @@ namespace Barotrauma }; filterToggle.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipHorizontally); + ternaryFilters = new Dictionary(); filterTickBoxes = new Dictionary(); filterSameVersion = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("FilterSameVersion")) @@ -382,40 +436,11 @@ namespace Barotrauma CanBeFocused = false }; - filterKarma = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.karma.true")) - { - UserData = TextManager.Get("servertag.karma.true"), - OnSelected = (tickBox) => { FilterServers(); return true; } - }; - filterTickBoxes.Add("servertag.karma", filterKarma); - - filterTraitor = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.traitors.true")) - { - UserData = TextManager.Get("servertag.traitors.true"), - OnSelected = (tickBox) => { FilterServers(); return true; } - }; - filterTickBoxes.Add("servertag.traitors", filterTraitor); - - filterFriendlyFire = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.friendlyfire.false")) - { - UserData = TextManager.Get("servertag.friendlyfire.false"), - OnSelected = (tickBox) => { FilterServers(); return true; } - }; - filterTickBoxes.Add("servertag.friendlyfire", filterFriendlyFire); - - filterVoip = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.voip.false")) - { - UserData = TextManager.Get("servertag.voip.false"), - OnSelected = (tickBox) => { FilterServers(); return true; } - }; - filterTickBoxes.Add("servertag.voip", filterVoip); - - filterModded = new GUITickBox(new RectTransform(new Vector2(1.0f, elementHeight), filters.Content.RectTransform), TextManager.Get("servertag.modded.true")) - { - UserData = TextManager.Get("servertag.modded.true"), - OnSelected = (tickBox) => { FilterServers(); return true; } - }; - filterTickBoxes.Add("servertag.modded", filterModded); + AddTernaryFilter(filters.Content.RectTransform, elementHeight, "karma", (value) => { filterKarmaValue = value; }); + AddTernaryFilter(filters.Content.RectTransform, elementHeight, "traitors", (value) => { filterTraitorValue = value; }); + AddTernaryFilter(filters.Content.RectTransform, elementHeight, "friendlyfire", (value) => { filterFriendlyFireValue = value; }); + AddTernaryFilter(filters.Content.RectTransform, elementHeight, "voip", (value) => { filterVoipValue = value; }); + AddTernaryFilter(filters.Content.RectTransform, elementHeight, "modded", (value) => { filterModdedValue = value; }); // Play Style Selection new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), filters.Content.RectTransform), TextManager.Get("ServerSettingsPlayStyle"), font: GUI.SubHeadingFont) @@ -1076,9 +1101,15 @@ namespace Barotrauma else { bool incompatible = - (!serverInfo.ContentPackageHashes.Any() && serverInfo.ContentPackagesMatch()) || + (serverInfo.ContentPackageHashes.Any() && !serverInfo.ContentPackagesMatch()) || (remoteVersion != null && !NetworkMember.IsCompatible(GameMain.Version, remoteVersion)); + var karmaFilterPassed = filterKarmaValue == TernaryOption.Any|| (filterKarmaValue == TernaryOption.Enabled) == serverInfo.KarmaEnabled; + var friendlyFireFilterPassed = filterFriendlyFireValue == TernaryOption.Any || (filterFriendlyFireValue == TernaryOption.Enabled) == serverInfo.FriendlyFireEnabled; + var traitorsFilterPassed = filterTraitorValue == TernaryOption.Any || (filterTraitorValue == TernaryOption.Enabled) == (serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe); + var voipFilterPassed = filterVoipValue == TernaryOption.Any || (filterVoipValue == TernaryOption.Enabled) == serverInfo.VoipEnabled; + var moddedFilterPassed = filterModdedValue == TernaryOption.Any || (filterModdedValue == TernaryOption.Enabled) == serverInfo.GetPlayStyleTags().Any(t => t.Contains("modded.true")); + child.Visible = serverInfo.OwnerVerified && serverInfo.ServerName.Contains(searchBox.Text, StringComparison.OrdinalIgnoreCase) && @@ -1089,11 +1120,11 @@ namespace Barotrauma (!filterEmpty.Selected || serverInfo.PlayerCount > 0) && (!filterWhitelisted.Selected || serverInfo.UsingWhiteList == true) && (!filterOffensive.Selected || !ForbiddenWordFilter.IsForbidden(serverInfo.ServerName)) && - (!filterKarma.Selected || serverInfo.KarmaEnabled == true) && - (!filterFriendlyFire.Selected || serverInfo.FriendlyFireEnabled == false) && - (!filterTraitor.Selected || serverInfo.TraitorsEnabled == YesNoMaybe.Yes || serverInfo.TraitorsEnabled == YesNoMaybe.Maybe) && - (!filterVoip.Selected || serverInfo.VoipEnabled == false) && - (!filterModded.Selected || serverInfo.GetPlayStyleTags().Any(t => t.Contains("modded.true"))) && + karmaFilterPassed && + friendlyFireFilterPassed && + traitorsFilterPassed && + voipFilterPassed && + moddedFilterPassed && ((selectedTab == ServerListTab.All && (serverInfo.LobbyID != 0 || !string.IsNullOrWhiteSpace(serverInfo.Port))) || (selectedTab == ServerListTab.Recent && serverInfo.Recent) || (selectedTab == ServerListTab.Favorites && serverInfo.Favorite)); @@ -2334,6 +2365,10 @@ namespace Barotrauma { element.Add(new XAttribute(filterBox.Key, filterBox.Value.Selected.ToString())); } + foreach (KeyValuePair ternaryFilter in ternaryFilters) + { + element.Add(new XAttribute(ternaryFilter.Key, ternaryFilter.Value.SelectedData.ToString())); + } } public void LoadServerFilters(XElement element) @@ -2344,6 +2379,15 @@ namespace Barotrauma { filterBox.Value.Selected = element.GetAttributeBool(filterBox.Key, filterBox.Value.Selected); } + foreach (KeyValuePair ternaryFilter in ternaryFilters) + { + string valueStr = element.GetAttributeString(ternaryFilter.Key, ""); + TernaryOption ternaryOption = (TernaryOption)ternaryFilter.Value.SelectedData; + Enum.TryParse(valueStr, true, out ternaryOption); + + var child = ternaryFilter.Value.ListBox.Content.GetChildByUserData(ternaryOption); + ternaryFilter.Value.Select(ternaryFilter.Value.ListBox.Content.GetChildIndex(child)); + } } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs index 274a3da32..74ccd5434 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SteamWorkshopScreen.cs @@ -410,9 +410,9 @@ namespace Barotrauma if (sub.HasTag(SubmarineTag.HideInMenus)) { continue; } string subPath = Path.GetFullPath(sub.FilePath); - //ignore subs that are part of the vanilla content package + //ignore files that are part of the vanilla content package if (GameMain.VanillaContent != null && - GameMain.VanillaContent.GetFilesOfType(ContentType.Submarine).Any(s => Path.GetFullPath(s) == subPath)) + GameMain.VanillaContent.Files.Any(s => Path.GetFullPath(s.Path) == subPath)) { continue; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index dca0fe9f0..318acacae 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -870,7 +870,7 @@ namespace Barotrauma GameMain.GameScreen.Select(); - GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, null); + GameSession gameSession = new GameSession(backedUpSubInfo, "", GameModePreset.TestMode, CampaignSettings.Empty, null); gameSession.StartRound(null, false); (gameSession.GameMode as TestGameMode).OnRoundEnd = () => { @@ -897,6 +897,8 @@ namespace Barotrauma foreach (MapEntityCategory category in Enum.GetValues(typeof(MapEntityCategory))) { + string categoryName = TextManager.Get("MapEntityCategory." + category); + maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(categoryName.Replace(' ', '\n')).X + GUI.IntScale(50)); foreach (MapEntityPrefab ep in MapEntityPrefab.List) { if (!ep.Category.HasFlag(category)) { continue; } @@ -907,13 +909,14 @@ namespace Barotrauma } entityLists[category + ep.Subcategory].Add(ep); categoryKeys[category + ep.Subcategory] = category; - string categoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory; - if (categoryName != null) + string subcategoryName = TextManager.Get("subcategory." + ep.Subcategory, returnNull: true) ?? ep.Subcategory; + if (subcategoryName != null) { - maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(categoryName.Replace(' ', '\n')).X + GUI.IntScale(50)); + maxTextWidth = (int)Math.Max(maxTextWidth, GUI.SubHeadingFont.MeasureString(subcategoryName.Replace(' ', '\n')).X + GUI.IntScale(50)); } } } + categorizedEntityList.Content.ClampMouseRectToParent = true; int entitiesPerRow = (int)Math.Ceiling(categorizedEntityList.Content.Rect.Width / Math.Max(125 * GUI.Scale, 60)); foreach (string categoryKey in entityLists.Keys) @@ -926,19 +929,33 @@ namespace Barotrauma new GUIFrame(new RectTransform(Vector2.One, categoryFrame.RectTransform), style: "HorizontalLine"); - string categoryName = entityLists[categoryKey].First().Subcategory; - categoryName = string.IsNullOrEmpty(categoryName) ? - TextManager.Get("mapentitycategory.misc") : - (TextManager.Get("subcategory." + categoryName, returnNull: true) ?? categoryName); - new GUITextBlock( - new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft), - categoryName, - textAlignment: Alignment.TopLeft, - font: GUI.SubHeadingFont, - wrap: true) + string categoryName = TextManager.Get("MapEntityCategory." + entityLists[categoryKey].First().Category); + string subCategoryName = entityLists[categoryKey].First().Subcategory; + if (string.IsNullOrEmpty(subCategoryName)) { - Padding = new Vector4(GUI.IntScale(10)) - }; + new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft), + categoryName, textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont, wrap: true) + { + Padding = new Vector4(GUI.IntScale(10)) + }; + + } + else + { + subCategoryName = string.IsNullOrEmpty(subCategoryName) ? + TextManager.Get("mapentitycategory.misc") : + (TextManager.Get("subcategory." + subCategoryName, returnNull: true) ?? subCategoryName); + var categoryTitle = new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft), + categoryName, textAlignment: Alignment.TopLeft, font: GUI.Font, wrap: true) + { + Padding = new Vector4(GUI.IntScale(10)) + }; + new GUITextBlock(new RectTransform(new Point(maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(0, (int)(categoryTitle.TextSize.Y + GUI.IntScale(10))) }, + subCategoryName, textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont, wrap: true) + { + Padding = new Vector4(GUI.IntScale(10)) + }; + } var entityListInner = new GUIListBox(new RectTransform(new Point(categoryFrame.Rect.Width - maxTextWidth, categoryFrame.Rect.Height), categoryFrame.RectTransform, Anchor.CenterRight), style: null, @@ -959,7 +976,6 @@ namespace Barotrauma #if !DEBUG if (ep.HideInMenus) { continue; } #endif - CreateEntityElement(ep, entitiesPerRow, entityListInner.Content); } @@ -976,6 +992,9 @@ namespace Barotrauma foreach (MapEntityPrefab ep in MapEntityPrefab.List) { +#if !DEBUG + if (ep.HideInMenus) { continue; } +#endif CreateEntityElement(ep, entitiesPerRow, allEntityList.Content); } } @@ -1354,7 +1373,7 @@ namespace Barotrauma { try { - Barotrauma.IO.Validation.DevException = true; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; TimeSpan time = DateTime.UtcNow - DateTime.MinValue; string filePath = Path.Combine(autoSavePath, $"AutoSave_{(ulong)time.TotalMilliseconds}.sub"); SaveUtil.CompressStringToFile(filePath, doc.ToString()); @@ -1390,7 +1409,7 @@ namespace Barotrauma } }); - Barotrauma.IO.Validation.DevException = false; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; CrossThread.RequestExecutionOnMainThread(DisplayAutoSavePrompt); } catch (Exception e) @@ -1558,9 +1577,9 @@ namespace Barotrauma msgBox.Buttons[0].OnClicked = (bt, userdata) => { contentPackage.AddFile(savePath, ContentType.OutpostModule); - Barotrauma.IO.Validation.DevException = true; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; contentPackage.Save(contentPackage.Path, reload: false); - Barotrauma.IO.Validation.DevException = false; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; msgBox.Close(); return true; }; @@ -1635,7 +1654,7 @@ namespace Barotrauma if (Submarine.MainSub != null) { - Barotrauma.IO.Validation.DevException = true; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true; if (previewImage?.Sprite?.Texture != null && !previewImage.Sprite.Texture.IsDisposed && Submarine.MainSub.Info.Type != SubmarineType.OutpostModule) { bool savePreviewImage = true; @@ -1655,7 +1674,7 @@ namespace Barotrauma { Submarine.MainSub.SaveAs(savePath); } - Barotrauma.IO.Validation.DevException = false; + Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false; Submarine.MainSub.CheckForErrors(); @@ -4695,6 +4714,7 @@ namespace Barotrauma } graphics.Clear(backgroundColor); + ImageManager.Draw(spriteBatch, cam); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: cam.Transform); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs index e2f733817..78cc554d5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/OggSound.cs @@ -63,8 +63,18 @@ namespace Barotrauma.Sounds ALFormat = reader.Channels == 1 ? Al.FormatMono16 : Al.FormatStereo16; SampleRate = reader.SampleRate; + if (Buffers != null && SoundBuffers.BuffersGenerated < SoundBuffers.MaxBuffers) + { + Buffers.RequestAlBuffers(); FillBuffers(); + } + } + + public override void FillBuffers() + { if (!Stream) { + reader.DecodedPosition = 0; + int bufferSize = (int)reader.TotalSamples * reader.Channels; float[] floatBuffer = new float[bufferSize]; @@ -86,7 +96,7 @@ namespace Barotrauma.Sounds CastBuffer(floatBuffer, shortBuffer, readSamples); - Al.BufferData(ALBuffer, ALFormat, shortBuffer, + Al.BufferData(Buffers.AlBuffer, ALFormat, shortBuffer, readSamples * sizeof(short), SampleRate); int alError = Al.GetError(); @@ -99,7 +109,7 @@ namespace Barotrauma.Sounds CastBuffer(floatBuffer, shortBuffer, readSamples); - Al.BufferData(ALMuffledBuffer, ALFormat, shortBuffer, + Al.BufferData(Buffers.AlMuffledBuffer, ALFormat, shortBuffer, readSamples * sizeof(short), SampleRate); alError = Al.GetError(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs index 8aadfa00b..46a8b977d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/Sound.cs @@ -52,16 +52,10 @@ namespace Barotrauma.Sounds } } - private uint alBuffer; - public uint ALBuffer + private SoundBuffers buffers; + public SoundBuffers Buffers { - get { return !Stream ? alBuffer : 0; } - } - - private uint alMuffledBuffer; - public uint ALMuffledBuffer - { - get { return !Stream ? alMuffledBuffer : 0; } + get { return !Stream ? buffers : null; } } public int ALFormat @@ -169,69 +163,20 @@ namespace Barotrauma.Sounds { if (!Stream) { - Al.GenBuffer(out alBuffer); - int alError = Al.GetError(); - if (alError != Al.NoError) - { - throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError)); - } - - if (!Al.IsBuffer(alBuffer)) - { - throw new Exception("Generated OpenAL buffer is invalid!"); - } - - Al.GenBuffer(out alMuffledBuffer); - alError = Al.GetError(); - if (alError != Al.NoError) - { - throw new Exception("Failed to create OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError)); - } - - if (!Al.IsBuffer(alMuffledBuffer)) - { - throw new Exception("Generated OpenAL buffer is invalid!"); - } + buffers = new SoundBuffers(this); } else { - alBuffer = 0; + buffers = null; } } + public virtual void FillBuffers() { } + public virtual void DeleteALBuffers() { Owner.KillChannels(this); - if (alBuffer != 0) - { - if (!Al.IsBuffer(alBuffer)) - { - throw new Exception("Buffer to delete is invalid!"); - } - - Al.DeleteBuffer(alBuffer); alBuffer = 0; - - int alError = Al.GetError(); - if (alError != Al.NoError) - { - throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError)); - } - } - if (alMuffledBuffer != 0) - { - if (!Al.IsBuffer(alMuffledBuffer)) - { - throw new Exception("Buffer to delete is invalid!"); - } - - Al.DeleteBuffer(alMuffledBuffer); alMuffledBuffer = 0; - - int alError = Al.GetError(); - if (alError != Al.NoError) - { - throw new Exception("Failed to delete OpenAL buffer for non-streamed sound: " + Al.GetErrorString(alError)); - } - } + buffers?.Dispose(); } public virtual void Dispose() diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundChannel.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundChannel.cs index 4964a7053..0a530b6d5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundChannel.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundChannel.cs @@ -334,7 +334,11 @@ namespace Barotrauma.Sounds return; } - Al.Sourcei(alSource, Al.Buffer, muffled ? (int)Sound.ALMuffledBuffer : (int)Sound.ALBuffer); + if (Sound.Buffers.RequestAlBuffers()) + { + Sound.FillBuffers(); + } + Al.Sourcei(alSource, Al.Buffer, muffled ? (int)Sound.Buffers.AlMuffledBuffer : (int)Sound.Buffers.AlBuffer); alError = Al.GetError(); if (alError != Al.NoError) @@ -506,17 +510,26 @@ namespace Barotrauma.Sounds throw new Exception("Failed to reset source buffer: " + debugName + ", " + Al.GetErrorString(alError)); } - if (!Al.IsBuffer(sound.ALBuffer)) + if (Sound.Buffers.RequestAlBuffers()) + { + Sound.FillBuffers(); + } + + if (!Al.IsBuffer(sound.Buffers.AlBuffer)) { throw new Exception(sound.Filename + " has an invalid buffer!"); } + if (!Al.IsBuffer(sound.Buffers.AlMuffledBuffer)) + { + throw new Exception(sound.Filename + " has an invalid muffled buffer!"); + } - uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer; + uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffled ? Sound.Buffers.AlMuffledBuffer : Sound.Buffers.AlBuffer; Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer); alError = Al.GetError(); if (alError != Al.NoError) { - throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + sound.ALBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError)); + throw new Exception("Failed to bind buffer to source (" + ALSourceIndex.ToString() + ":" + sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex) + "," + alBuffer.ToString() + "): " + debugName + ", " + Al.GetErrorString(alError)); } Al.SourcePlay(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex)); @@ -528,7 +541,7 @@ namespace Barotrauma.Sounds } else { - uint alBuffer = sound.Owner.GetCategoryMuffle(category) || muffle ? sound.ALMuffledBuffer : sound.ALBuffer; + uint alBuffer = 0; Al.Sourcei(sound.Owner.GetSourceFromIndex(Sound.SourcePoolIndex, ALSourceIndex), Al.Buffer, (int)alBuffer); int alError = Al.GetError(); if (alError != Al.NoError) diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs index 1718a33f3..784132b40 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundManager.cs @@ -30,6 +30,8 @@ namespace Barotrauma.Sounds private readonly SoundSourcePool[] sourcePools; private readonly List loadedSounds; + public IReadOnlyList LoadedSounds => loadedSounds; + private readonly SoundChannel[][] playingChannels = new SoundChannel[2][]; private readonly object threadDeathMutex = new object(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs b/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs index d2d8f6028..c4e17d9b7 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sprite/Sprite.cs @@ -172,22 +172,22 @@ namespace Barotrauma return null; } - public void Draw(SpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None) + public void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None) { this.Draw(spriteBatch, pos, Color.White, rotate, scale, spriteEffect); } - public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) + public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) { this.Draw(spriteBatch, pos, color, this.origin, rotate, new Vector2(scale, scale), spriteEffect, depth); } - public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) + public void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) { this.Draw(spriteBatch, pos, color, origin, rotate, new Vector2(scale, scale), spriteEffect, depth); } - public virtual void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) + public virtual void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = null) { if (Texture == null) { return; } //DrawSilhouette(spriteBatch, pos, origin, rotate, scale, spriteEffect, depth); @@ -209,7 +209,7 @@ namespace Barotrauma } } - public void DrawTiled(SpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, + public void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, Color? color = null, Vector2? startOffset = null, Vector2? textureScale = null, float? depth = null) { if (Texture == null) { return; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs b/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs index 9538ca7f3..105bb81bf 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sprite/SpriteSheet.cs @@ -5,14 +5,14 @@ namespace Barotrauma { partial class SpriteSheet : Sprite { - public override void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) + public override void Draw(ISpriteBatch spriteBatch, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) { if (texture == null) return; spriteBatch.Draw(texture, pos + offset, sourceRects[0], color, rotation + rotate, origin, scale, spriteEffect, depth == null ? this.depth : (float)depth); } - public void Draw(SpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) + public void Draw(ISpriteBatch spriteBatch, int spriteIndex, Vector2 pos, Color color, Vector2 origin, float rotate, Vector2 scale, SpriteEffects spriteEffect = SpriteEffects.None, float? depth = default(float?)) { if (texture == null) return; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs b/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs index c209095aa..d5847be30 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Utils/ToolBox.cs @@ -100,38 +100,6 @@ namespace Barotrauma return q1; } - /// - /// Convert a HSV value into a RGB value. - /// - /// Value between 0 and 360 - /// Value between 0 and 1 - /// Value between 0 and 1 - /// Reference - /// - public static Color HSVToRGB(float hue, float saturation, float value) - { - float c = value * saturation; - - float h = Math.Clamp(hue, 0, 360) / 60f; - - float x = c * (1 - Math.Abs(h % 2 - 1)); - - float r = 0, - g = 0, - b = 0; - - if (0 <= h && h <= 1) { r = c; g = x; b = 0; } - else if (1 < h && h <= 2) { r = x; g = c; b = 0; } - else if (2 < h && h <= 3) { r = 0; g = c; b = x; } - else if (3 < h && h <= 4) { r = 0; g = x; b = c; } - else if (4 < h && h <= 5) { r = x; g = 0; b = c; } - else if (5 < h && h <= 6) { r = c; g = 0; b = x; } - - float m = value - c; - - return new Color(r + m, g + m, b + m); - } - /// /// Convert a RGB value into a HSV value. /// diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 582197662..3a0287f83 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.0 + 0.1300.0.1 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index f1b452084..16dc48e10 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.0 + 0.1300.0.1 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index df309ecde..75213238a 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.0 + 0.1300.0.1 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/app.manifest b/Barotrauma/BarotraumaClient/app.manifest index ff31a5f7c..54b3a73be 100644 --- a/Barotrauma/BarotraumaClient/app.manifest +++ b/Barotrauma/BarotraumaClient/app.manifest @@ -1,62 +1,62 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true/pm - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + + + + + diff --git a/Barotrauma/BarotraumaClient/libSDL2-2.0.0.dylib b/Barotrauma/BarotraumaClient/libSDL2-2.0.0.dylib old mode 100644 new mode 100755 diff --git a/Barotrauma/BarotraumaClient/libfreetype6.dylib b/Barotrauma/BarotraumaClient/libfreetype6.dylib old mode 100644 new mode 100755 diff --git a/Barotrauma/BarotraumaClient/libwebm_mem_playback_x64.dylib b/Barotrauma/BarotraumaClient/libwebm_mem_playback_x64.dylib old mode 100644 new mode 100755 diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 144798777..bcf39f215 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -1,139 +1,139 @@ - - - - Exe - netcoreapp3.1 - Barotrauma - FakeFish, Undertow Games - Barotrauma Dedicated Server - 0.1300.0.0 - Copyright © FakeFish 2018-2020 - AnyCPU;x64 - DedicatedServer - ..\BarotraumaShared\Icon.ico - Debug;Release;Unstable - - - - DEBUG;TRACE;SERVER;LINUX;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - TRACE;SERVER;LINUX;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - TRACE;SERVER;LINUX;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - TRACE;SERVER;LINUX;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - TRACE;SERVER;LINUX;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Linux\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(IntermediateOutputPath)gitver - $(IntermediateOutputPath)gitbranch - - - - - - - - - - - - - - - - - - @(GitVersion) - - - - - - - - - @(GitBranch) - - - - - - - $(IntermediateOutputPath)CustomAssemblyInfo.cs - - - - - - - - - <_Parameter1>GitRevision - <_Parameter2>$(BuildHash) - - - <_Parameter1>GitBranch - <_Parameter2>$(BuildBranch) - - - <_Parameter1>ProjectDir - <_Parameter2>$(ProjectDir) - - - - - - - + + + + Exe + netcoreapp3.1 + Barotrauma + FakeFish, Undertow Games + Barotrauma Dedicated Server + 0.1300.0.1 + Copyright © FakeFish 2018-2020 + AnyCPU;x64 + DedicatedServer + ..\BarotraumaShared\Icon.ico + Debug;Release;Unstable + + + + DEBUG;TRACE;SERVER;LINUX;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + TRACE;DEBUG;SERVER;LINUX;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + TRACE;SERVER;LINUX;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + TRACE;SERVER;LINUX;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + TRACE;SERVER;LINUX;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + TRACE;SERVER;LINUX;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Linux\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(IntermediateOutputPath)gitver + $(IntermediateOutputPath)gitbranch + + + + + + + + + + + + + + + + + + @(GitVersion) + + + + + + + + + @(GitBranch) + + + + + + + $(IntermediateOutputPath)CustomAssemblyInfo.cs + + + + + + + + + <_Parameter1>GitRevision + <_Parameter2>$(BuildHash) + + + <_Parameter1>GitBranch + <_Parameter2>$(BuildBranch) + + + <_Parameter1>ProjectDir + <_Parameter2>$(ProjectDir) + + + + + + + diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 948c1a039..9567b598f 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -1,152 +1,152 @@ - - - - Exe - netcoreapp3.1 - Barotrauma - FakeFish, Undertow Games - Barotrauma Dedicated Server - 0.1300.0.0 - Copyright © FakeFish 2018-2020 - AnyCPU;x64 - DedicatedServer - ..\BarotraumaShared\Icon.ico - 0.9.0.0 - Debug;Release;Unstable - - - - TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0 - x64 - ..\bin\DebugMac - true - - - - - TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Mac\ - - - - TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0 - x64 - - ..\bin\ReleaseMac - - - - TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE - x64 - - ..\bin\ReleaseMac - - - - TRACE;SERVER;OSX;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Mac\ - - - - TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE - x64 - ..\bin\$(Configuration)Mac\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - libsteam_api64.dylib - PreserveNewest - - - - - - $(IntermediateOutputPath)gitver - $(IntermediateOutputPath)gitbranch - - - - - - - - - - - - - - - - - - @(GitVersion) - - - - - - - - - @(GitBranch) - - - - - - - $(IntermediateOutputPath)CustomAssemblyInfo.cs - - - - - - - - - <_Parameter1>GitRevision - <_Parameter2>$(BuildHash) - - - <_Parameter1>GitBranch - <_Parameter2>$(BuildBranch) - - - <_Parameter1>ProjectDir - <_Parameter2>$(ProjectDir) - - - - - - - + + + + Exe + netcoreapp3.1 + Barotrauma + FakeFish, Undertow Games + Barotrauma Dedicated Server + 0.1300.0.1 + Copyright © FakeFish 2018-2020 + AnyCPU;x64 + DedicatedServer + ..\BarotraumaShared\Icon.ico + 0.9.0.0 + Debug;Release;Unstable + + + + TRACE;SERVER;OSX;USE_STEAM;DEBUG;NETCOREAPP;NETCOREAPP3_0 + x64 + ..\bin\DebugMac + true + + + + + TRACE;DEBUG;SERVER;OSX;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Mac\ + + + + TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0 + x64 + + ..\bin\ReleaseMac + + + + TRACE;SERVER;OSX;USE_STEAM;RELEASE;NETCOREAPP;NETCOREAPP3_0;UNSTABLE + x64 + + ..\bin\ReleaseMac + + + + TRACE;SERVER;OSX;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Mac\ + + + + TRACE;SERVER;OSX;X64;USE_STEAM;UNSTABLE + x64 + ..\bin\$(Configuration)Mac\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + libsteam_api64.dylib + PreserveNewest + + + + + + $(IntermediateOutputPath)gitver + $(IntermediateOutputPath)gitbranch + + + + + + + + + + + + + + + + + + @(GitVersion) + + + + + + + + + @(GitBranch) + + + + + + + $(IntermediateOutputPath)CustomAssemblyInfo.cs + + + + + + + + + <_Parameter1>GitRevision + <_Parameter2>$(BuildHash) + + + <_Parameter1>GitBranch + <_Parameter2>$(BuildBranch) + + + <_Parameter1>ProjectDir + <_Parameter2>$(ProjectDir) + + + + + + + diff --git a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterInfo.cs index 0aa71f569..f4d7baa52 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Characters/CharacterInfo.cs @@ -26,6 +26,7 @@ namespace Barotrauma { msg.Write(ID); msg.Write(Name); + msg.Write(OriginalName); msg.Write((byte)Gender); msg.Write((byte)Race); msg.Write((byte)HeadSpriteId); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/AbandonedOutpostMission.cs b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/AbandonedOutpostMission.cs index 1a978d069..89446aa82 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/AbandonedOutpostMission.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Events/Missions/AbandonedOutpostMission.cs @@ -17,6 +17,8 @@ namespace Barotrauma foreach (Character character in characters) { character.WriteSpawnData(msg, character.ID, restrictMessageSize: false); + msg.Write(requireKill.Contains(character)); + msg.Write(requireRescue.Contains(character)); msg.Write((ushort)characterItems[character].Count()); foreach (Item item in characterItems[character]) { diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index bdc4e9f7d..7bd0c9b6e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -32,11 +32,11 @@ namespace Barotrauma get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); } } - public static void StartNewCampaign(string savePath, string subPath, string seed) + public static void StartNewCampaign(string savePath, string subPath, string seed, CampaignSettings settings) { if (string.IsNullOrWhiteSpace(savePath)) { return; } - GameMain.GameSession = new GameSession(new SubmarineInfo(subPath), savePath, GameModePreset.MultiPlayerCampaign, seed); + GameMain.GameSession = new GameSession(new SubmarineInfo(subPath), savePath, GameModePreset.MultiPlayerCampaign, settings, seed); GameMain.NetLobbyScreen.ToggleCampaignMode(true); SaveUtil.SaveGame(GameMain.GameSession.SavePath); @@ -74,7 +74,7 @@ namespace Barotrauma DebugConsole.ShowQuestionPrompt("Enter a save name for the campaign:", (string saveName) => { string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName); - StartNewCampaign(savePath, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed); + StartNewCampaign(savePath, GameMain.NetLobbyScreen.SelectedSub.FilePath, GameMain.NetLobbyScreen.LevelSeed, CampaignSettings.Empty); }); } else @@ -667,13 +667,24 @@ namespace Barotrauma } bool validateHires = msg.ReadBoolean(); - bool fireCharacter = msg.ReadBoolean(); + bool renameCharacter = msg.ReadBoolean(); + int renamedIdentifier = -1; + string newName = null; + bool existingCrewMember = false; + if (renameCharacter) + { + renamedIdentifier = msg.ReadInt32(); + newName = msg.ReadString(); + existingCrewMember = msg.ReadBoolean(); + } + + bool fireCharacter = msg.ReadBoolean(); int firedIdentifier = -1; if (fireCharacter) { firedIdentifier = msg.ReadInt32(); } Location location = map?.CurrentLocation; - + List hiredCharacters = new List(); CharacterInfo firedCharacter = null; if (location != null && AllowedToManageCampaign(sender)) @@ -691,13 +702,45 @@ namespace Barotrauma } } + if (renameCharacter) + { + CharacterInfo characterInfo = null; + if (existingCrewMember && CrewManager != null) + { + characterInfo = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier); + } + else if(!existingCrewMember && location.HireManager != null) + { + characterInfo = location.HireManager.AvailableCharacters.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier); + } + + if (characterInfo != null && (characterInfo.Character?.IsBot ?? true)) + { + if (existingCrewMember) + { + CrewManager.RenameCharacter(characterInfo, newName); + } + else + { + location.HireManager.RenameCharacter(characterInfo, newName); + } + } + else + { + DebugConsole.ThrowError($"Tried to rename an invalid character ({renamedIdentifier})"); + } + } + if (location.HireManager != null) { if (validateHires) { foreach (CharacterInfo hireInfo in location.HireManager.PendingHires) { - TryHireCharacter(location, hireInfo); + if (TryHireCharacter(location, hireInfo)) + { + hiredCharacters.Add(hireInfo); + }; } } @@ -706,10 +749,10 @@ namespace Barotrauma List pendingHireInfos = new List(); foreach (int identifier in pendingHires) { - CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifier() == identifier); + CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == identifier); if (match == null) { - DebugConsole.ThrowError($"Tried to hire a character that doesn't exist ({identifier})"); + DebugConsole.ThrowError($"Tried to add a character that doesn't exist ({identifier}) to pending hires"); continue; } @@ -721,25 +764,39 @@ namespace Barotrauma } location.HireManager.PendingHires = pendingHireInfos; } + + location.HireManager.AvailableCharacters.ForEachMod(info => + { + if(!location.HireManager.PendingHires.Contains(info)) + { + location.HireManager.RenameCharacter(info, info.OriginalName); + } + }); } } // bounce back - SendCrewState(validateHires, firedCharacter); + if (renameCharacter && existingCrewMember) + { + SendCrewState(hiredCharacters, (renamedIdentifier, newName), firedCharacter); + } + else + { + SendCrewState(hiredCharacters, default, firedCharacter); + } } /// /// Notifies the clients of the current bot situation like syncing pending and available hires - /// available hires are also synced /// - /// When set to true notifies the clients that the hires have been validated. - /// When not null will inform the clients that his character has been fired. + /// Inform the clients that these characters have been hired. + /// Inform the clients that this character has been fired. /// /// It might be obsolete to sync available hires. I found that the available hires are always the same between /// the client and the server when there's only one person on the server but when a second person joins both of /// their available hires are different from the server. /// - public void SendCrewState(bool validateHires, CharacterInfo firedCharacter) + public void SendCrewState(List hiredCharacters, (int id, string newName) renamedCrewMember, CharacterInfo firedCharacter) { List availableHires = new List(); List pendingHires = new List(); @@ -765,10 +822,26 @@ namespace Barotrauma msg.Write((ushort)pendingHires.Count); foreach (CharacterInfo pendingHire in pendingHires) { - msg.Write(pendingHire.GetIdentifier()); + msg.Write(pendingHire.GetIdentifierUsingOriginalName()); + } + + msg.Write((ushort)(hiredCharacters?.Count ?? 0)); + if(hiredCharacters != null) + { + foreach (CharacterInfo info in hiredCharacters) + { + info.ServerWrite(msg); + msg.Write(info.Salary); + } + } + + bool validRenaming = renamedCrewMember.id > -1 && !string.IsNullOrEmpty(renamedCrewMember.newName); + msg.Write(validRenaming); + if (validRenaming) + { + msg.Write(renamedCrewMember.id); + msg.Write(renamedCrewMember.newName); } - - msg.Write(validateHires); msg.Write(firedCharacter != null); if (firedCharacter != null) { msg.Write(firedCharacter.GetIdentifier()); } @@ -786,6 +859,7 @@ namespace Barotrauma new XAttribute("purchasedhullrepairs", PurchasedHullRepairs), new XAttribute("purchaseditemrepairs", PurchasedItemRepairs), new XAttribute("cheatsenabled", CheatsEnabled)); + modeElement.Add(Settings.Save()); CampaignMetadata?.Save(modeElement); Map.Save(modeElement); CargoManager?.SavePurchasedItems(modeElement); diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs index a9ece25b7..4a95adecd 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/BanList.cs @@ -121,13 +121,14 @@ namespace Barotrauma.Networking } } - public bool IsBanned(IPAddress IP, ulong steamID, out string reason) + public bool IsBanned(IPAddress IP, ulong steamID, ulong ownerSteamID, out string reason) { reason = string.Empty; if (IPAddress.IsLoopback(IP)) { return false; } var bannedPlayer = bannedPlayers.Find(bp => bp.CompareTo(IP) || - (steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID))); + (steamID > 0 && (bp.SteamID == steamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == steamID)) || + (ownerSteamID > 0 && (bp.SteamID == ownerSteamID || SteamManager.SteamIDStringToUInt64(bp.EndPoint) == ownerSteamID))); reason = bannedPlayer?.Reason; return bannedPlayer != null; } @@ -166,6 +167,7 @@ namespace Barotrauma.Networking public void BanPlayer(string name, ulong steamID, string reason, TimeSpan? duration) { + if (steamID == 0) { return; } BanPlayer(name, "", steamID, reason, duration); } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index 27b949beb..062719abb 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -284,6 +284,7 @@ namespace Barotrauma.Networking newClient.Connection = connection; newClient.Connection.Status = NetworkConnectionStatus.Connected; newClient.SteamID = connection.SteamID; + newClient.OwnerSteamID = connection.OwnerSteamID; newClient.Language = connection.Language; ConnectedClients.Add(newClient); @@ -308,12 +309,13 @@ namespace Barotrauma.Networking SendConsoleMessage("Granted all permissions to " + newClient.Name + ".", newClient); } - SendChatMessage($"ServerMessage.JoinedServer~[client]={clName}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined); + SendChatMessage($"ServerMessage.JoinedServer~[client]={ClientLogName(newClient)}", ChatMessageType.Server, null, changeType: PlayerConnectionChangeType.Joined); serverSettings.ServerDetailsChanged = true; if (previousPlayer != null && previousPlayer.Name != newClient.Name) { - SendChatMessage($"ServerMessage.PreviousClientName~[client]={clName}~[previousname]={previousPlayer.Name}", ChatMessageType.Server, null); + string prevNameSanitized = previousPlayer.Name.Replace("‖", ""); + SendChatMessage($"ServerMessage.PreviousClientName~[client]={ClientLogName(newClient)}~[previousname]={prevNameSanitized}", ChatMessageType.Server, null); previousPlayer.Name = newClient.Name; } @@ -475,12 +477,14 @@ namespace Barotrauma.Networking } else if (isCrewDead && respawnManager == null) { +#if !DEBUG if (endRoundTimer <= 0.0f) { SendChatMessage(TextManager.GetWithVariable("CrewDeadNoRespawns", "[time]", "60"), ChatMessageType.Server); } endRoundDelay = 60.0f; endRoundTimer += deltaTime; +#endif } else { @@ -752,6 +756,7 @@ namespace Barotrauma.Networking string seed = inc.ReadString(); string subName = inc.ReadString(); string subHash = inc.ReadString(); + CampaignSettings settings = new CampaignSettings(inc); var matchingSub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName && s.MD5Hash.Hash == subHash); @@ -772,10 +777,10 @@ namespace Barotrauma.Networking string localSavePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer, saveName); if (connectedClient.HasPermission(ClientPermissions.SelectMode) || connectedClient.HasPermission(ClientPermissions.ManageCampaign)) { - MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed); + MultiPlayerCampaign.StartNewCampaign(localSavePath, matchingSub.FilePath, seed, settings); } } - } + } else { string saveName = inc.ReadString(); @@ -1853,6 +1858,8 @@ namespace Barotrauma.Networking { outmsg.Write(autoRestartTimerRunning ? serverSettings.AutoRestartTimer : 0.0f); } + + outmsg.Write(serverSettings.RadiationEnabled); } else { @@ -2034,12 +2041,12 @@ namespace Barotrauma.Networking } } - startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(selectedSub, selectedShuttle, selectedMode), false); + startGameCoroutine = GameMain.Instance.ShowLoading(StartGame(selectedSub, selectedShuttle, selectedMode, CampaignSettings.Unsure), false); yield return CoroutineStatus.Success; } - private IEnumerable StartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode) + private IEnumerable StartGame(SubmarineInfo selectedSub, SubmarineInfo selectedShuttle, GameModePreset selectedMode, CampaignSettings settings) { entityEventManager.Clear(); @@ -2067,7 +2074,7 @@ namespace Barotrauma.Networking //don't instantiate a new gamesession if we're playing a campaign if (campaign == null || GameMain.GameSession == null) { - GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, GameMain.NetLobbyScreen.LevelSeed, missionType: GameMain.NetLobbyScreen.MissionType); + GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, settings, GameMain.NetLobbyScreen.LevelSeed, missionType: GameMain.NetLobbyScreen.MissionType); } List playingClients = new List(connectedClients); @@ -2716,6 +2723,10 @@ namespace Barotrauma.Networking { serverSettings.BanList.BanPlayer(client.Name, client.SteamID, reason, duration); } + if (client.OwnerSteamID > 0) + { + serverSettings.BanList.BanPlayer(client.Name, client.OwnerSteamID, reason, duration); + } } public void BanPreviousPlayer(PreviousPlayer previousPlayer, string reason, bool range = false, TimeSpan? duration = null) @@ -2735,6 +2746,10 @@ namespace Barotrauma.Networking { serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.SteamID, reason, duration); } + if (previousPlayer.OwnerSteamID > 0) + { + serverSettings.BanList.BanPlayer(previousPlayer.Name, previousPlayer.OwnerSteamID, reason, duration); + } string msg = $"ServerMessage.BannedFromServer~[client]={previousPlayer.Name}"; if (!string.IsNullOrWhiteSpace(reason)) @@ -2784,7 +2799,7 @@ namespace Barotrauma.Networking client.HasSpawned = false; client.InGame = false; - if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={client.Name}"; } + if (string.IsNullOrWhiteSpace(msg)) { msg = $"ServerMessage.ClientLeftServer~[client]={ClientLogName(client)}"; } if (string.IsNullOrWhiteSpace(targetmsg)) { targetmsg = "ServerMessage.YouLeftServer"; } if (!string.IsNullOrWhiteSpace(reason)) { @@ -3747,7 +3762,7 @@ namespace Barotrauma.Networking { retVal += "color:#ff9900;"; } - retVal += "metadata:" + (client.SteamID != 0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name) + "‖end‖"; + retVal += "metadata:" + (client.SteamID != 0 ? client.SteamID.ToString() : client.ID.ToString()) + "‖" + (name ?? client.Name).Replace("‖","") + "‖end‖"; return retVal; } @@ -3826,6 +3841,7 @@ namespace Barotrauma.Networking public string Name; public string EndPoint; public UInt64 SteamID; + public UInt64 OwnerSteamID; public float Karma; public int KarmaKickCount; public readonly List KickVoters = new List(); @@ -3835,6 +3851,7 @@ namespace Barotrauma.Networking Name = c.Name; EndPoint = c.Connection?.EndPointString ?? ""; SteamID = c.SteamID; + OwnerSteamID = c.OwnerSteamID; } public bool MatchesClient(Client c) diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs index 8d75fa8f4..84d18bbe5 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/LidgrenServerPeer.cs @@ -184,7 +184,7 @@ namespace Barotrauma.Networking return; } - if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, out string banReason)) + if (serverSettings.BanList.IsBanned(inc.SenderConnection.RemoteEndPoint.Address, 0, 0, out string banReason)) { //IP banned: deny immediately inc.SenderConnection.Deny(DisconnectReason.Banned.ToString() + "/ " + banReason); @@ -233,7 +233,7 @@ namespace Barotrauma.Networking return; } if (pendingClient != null) { pendingClients.Remove(pendingClient); } - if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, out string banReason)) + if (serverSettings.BanList.IsBanned(conn.IPEndPoint.Address, conn.SteamID, conn.OwnerSteamID, out string banReason)) { Disconnect(conn, DisconnectReason.Banned.ToString() + "/ " + banReason); return; @@ -308,7 +308,8 @@ namespace Barotrauma.Networking } LidgrenConnection pendingConnection = pendingClient.Connection as LidgrenConnection; - if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, out string banReason)) + string banReason; + if (serverSettings.BanList.IsBanned(pendingConnection.NetConnection.RemoteEndPoint.Address, steamID, ownerID, out banReason)) { RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason); return; @@ -316,6 +317,7 @@ namespace Barotrauma.Networking if (status == Steamworks.AuthResponse.OK) { + pendingClient.OwnerSteamID = ownerID; pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder; pendingClient.UpdateTime = Timing.TotalTime; } @@ -452,7 +454,7 @@ namespace Barotrauma.Networking pendingClient.AuthSessionStarted = true; } } - else //TODO: could remove since this seems impossible + else { if (pendingClient.SteamID != steamId) { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/ServerPeer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/ServerPeer.cs index bfabe7aa8..a392f34fe 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/ServerPeer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/ServerPeer.cs @@ -49,6 +49,16 @@ namespace Barotrauma.Networking Connection.SetSteamIDIfUnknown(value ?? 0); } } + private UInt64? ownerSteamId; + public UInt64? OwnerSteamID + { + get { return ownerSteamId; } + set + { + ownerSteamId = value; + Connection.SetOwnerSteamIDIfUnknown(value ?? 0); + } + } public Int32? PasswordSalt; public bool AuthSessionStarted; @@ -59,6 +69,7 @@ namespace Barotrauma.Networking InitializationStep = ConnectionInitialization.SteamTicketAndVersion; Retries = 0; SteamID = null; + OwnerSteamID = null; PasswordSalt = null; UpdateTime = Timing.TotalTime + Timing.Step * 3.0; TimeOut = NetworkConnection.TimeoutThreshold; @@ -107,8 +118,8 @@ namespace Barotrauma.Networking RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion, $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}"); - GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); - DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); + GameServer.Log($"{name} ({steamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); + DebugConsole.NewMessage($"{name} ({steamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); return; } @@ -119,7 +130,7 @@ namespace Barotrauma.Networking if (nameTaken != null) { RemovePendingClient(pendingClient, DisconnectReason.NameTaken, ""); - GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error); + GameServer.Log($"{name} ({steamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error); return; } @@ -172,6 +183,7 @@ namespace Barotrauma.Networking else if (pendingClient.Connection is SteamP2PConnection s) { serverSettings.BanList.BanPlayer(pendingClient.Name, s.SteamID, banReason, duration); + serverSettings.BanList.BanPlayer(pendingClient.Name, s.OwnerSteamID, banReason, duration); } } @@ -183,7 +195,8 @@ namespace Barotrauma.Networking } else if (pendingClient.Connection is SteamP2PConnection s) { - return serverSettings.BanList.IsBanned(s.SteamID, out banReason); + return serverSettings.BanList.IsBanned(s.SteamID, out banReason) || + serverSettings.BanList.IsBanned(s.OwnerSteamID, out banReason); } banReason = null; return false; @@ -273,6 +286,7 @@ namespace Barotrauma.Networking { Steam.SteamManager.StopAuthSession(pendingClient.SteamID.Value); pendingClient.SteamID = null; + pendingClient.OwnerSteamID = null; pendingClient.AuthSessionStarted = false; } } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs index c53cdff42..c7b85305c 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/Primitives/Peers/Server/SteamP2PServerPeer.cs @@ -123,6 +123,7 @@ namespace Barotrauma.Networking if (!started) { return; } UInt64 senderSteamId = inc.ReadUInt64(); + UInt64 ownerSteamId = inc.ReadUInt64(); byte incByte = inc.ReadByte(); bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; @@ -145,7 +146,9 @@ namespace Barotrauma.Networking pendingClient?.Heartbeat(); connectedClient?.Heartbeat(); - if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason)) + string banReason; + if (serverSettings.BanList.IsBanned(senderSteamId, out banReason) || + serverSettings.BanList.IsBanned(ownerSteamId, out banReason)) { if (pendingClient != null) { @@ -181,6 +184,10 @@ namespace Barotrauma.Networking if (pendingClient != null) { + if (ownerSteamId != 0) + { + pendingClient.Connection.SetOwnerSteamIDIfUnknown(ownerSteamId); + } ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null)); } else @@ -223,6 +230,7 @@ namespace Barotrauma.Networking { Language = GameMain.Config.Language }; + OwnerConnection.SetOwnerSteamIDIfUnknown(OwnerSteamID); OnInitializationComplete?.Invoke(OwnerConnection); } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs index 79baf5f76..4d4d61940 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/ServerSettings.cs @@ -159,6 +159,8 @@ namespace Barotrauma.Networking AutoRestart = autoRestart; } + RadiationEnabled = incMsg.ReadBoolean(); + changed |= true; } diff --git a/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs index 6be934070..c24ba4ba1 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Screens/NetLobbyScreen.cs @@ -11,6 +11,8 @@ namespace Barotrauma private SubmarineInfo selectedSub; private SubmarineInfo selectedShuttle; + public bool RadiationEnabled = true; + public SubmarineInfo SelectedSub { get { return selectedSub; } diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 2bad80822..794866b5f 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -1,147 +1,147 @@ - - - - Exe - netcoreapp3.1 - Barotrauma - FakeFish, Undertow Games - Barotrauma Dedicated Server - 0.1300.0.0 - Copyright © FakeFish 2018-2020 - AnyCPU;x64 - DedicatedServer - ..\BarotraumaShared\Icon.ico - Debug;Release;Unstable - - - - DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - - - - TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - full - true - - - - TRACE;SERVER;WINDOWS;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - - - - TRACE;SERVER;WINDOWS;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - - - - TRACE;SERVER;WINDOWS;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - full - true - - - - TRACE;SERVER;WINDOWS;X64;USE_STEAM - x64 - ..\bin\$(Configuration)Windows\ - full - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(IntermediateOutputPath)gitver - $(IntermediateOutputPath)gitbranch - - - - - - - - - - - - - - - - - - @(GitVersion) - - - - - - - - - @(GitBranch) - - - - - - - $(IntermediateOutputPath)CustomAssemblyInfo.cs - - - - - - - - - <_Parameter1>GitRevision - <_Parameter2>$(BuildHash) - - - <_Parameter1>GitBranch - <_Parameter2>$(BuildBranch) - - - <_Parameter1>ProjectDir - <_Parameter2>$(ProjectDir) - - - - - - - + + + + Exe + netcoreapp3.1 + Barotrauma + FakeFish, Undertow Games + Barotrauma Dedicated Server + 0.1300.0.1 + Copyright © FakeFish 2018-2020 + AnyCPU;x64 + DedicatedServer + ..\BarotraumaShared\Icon.ico + Debug;Release;Unstable + + + + DEBUG;TRACE;SERVER;WINDOWS;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + + + + TRACE;DEBUG;SERVER;WINDOWS;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + full + true + + + + TRACE;SERVER;WINDOWS;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + + + + TRACE;SERVER;WINDOWS;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + + + + TRACE;SERVER;WINDOWS;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + full + true + + + + TRACE;SERVER;WINDOWS;X64;USE_STEAM + x64 + ..\bin\$(Configuration)Windows\ + full + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(IntermediateOutputPath)gitver + $(IntermediateOutputPath)gitbranch + + + + + + + + + + + + + + + + + + @(GitVersion) + + + + + + + + + @(GitBranch) + + + + + + + $(IntermediateOutputPath)CustomAssemblyInfo.cs + + + + + + + + + <_Parameter1>GitRevision + <_Parameter2>$(BuildHash) + + + <_Parameter1>GitBranch + <_Parameter2>$(BuildBranch) + + + <_Parameter1>ProjectDir + <_Parameter2>$(ProjectDir) + + + + + + + diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index d351cd4cd..bb1ccd4e0 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -62,6 +62,7 @@ + @@ -248,4 +249,6 @@ + + \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AITarget.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AITarget.cs index 0bcee48f4..5de1618ac 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AITarget.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AITarget.cs @@ -232,8 +232,7 @@ namespace Barotrauma public bool IsWithinSector(Vector2 worldPosition) { - if (sectorRad >= MathHelper.TwoPi) return true; - + if (sectorRad >= MathHelper.TwoPi) { return true; } Vector2 diff = worldPosition - WorldPosition; return MathUtils.GetShortestAngle(MathUtils.VectorToAngle(diff), MathUtils.VectorToAngle(sectorDir)) <= sectorRad * 0.5f; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs index 335e8fe9c..fdaba966b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs @@ -298,7 +298,7 @@ namespace Barotrauma private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget)); private string GetTargetingTag(AITarget aiTarget) { - if (aiTarget.Entity == null) { return null; } + if (aiTarget?.Entity == null) { return null; } string targetingTag = null; if (aiTarget.Entity is Character targetCharacter) { @@ -377,6 +377,7 @@ namespace Barotrauma { if (DisableEnemyAI) { return; } base.Update(deltaTime); + UpdateTriggers(deltaTime); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); if (steeringManager == insideSteering) @@ -462,7 +463,7 @@ namespace Barotrauma { updateTargetsTimer -= deltaTime; } - else if (avoidTimer <= 0) + else if (avoidTimer <= 0 || activeTriggers.Any() && returnTimer <= 0) { CharacterParams.TargetParams targetingParams = null; UpdateTargets(Character, out targetingParams); @@ -1448,8 +1449,12 @@ namespace Barotrauma break; case AttackPattern.Circle: if (IsCoolDownRunning) { break; } - if (IsAttackRunning) { break; } + if (IsAttackRunning && CirclePhase != CirclePhase.Strike) { break; } if (selectedTargetingParams == null) { break; } + var targetSub = SelectedAiTarget.Entity?.Submarine; + if (targetSub == null) { break; } + float subSize = Math.Max(targetSub.Borders.Width, targetSub.Borders.Height) / 2; + float sqrDistToSub = Vector2.DistanceSquared(WorldPosition, targetSub.WorldPosition); switch (CirclePhase) { case CirclePhase.Start: @@ -1471,22 +1476,31 @@ namespace Barotrauma circleOffset = Rand.Vector(MathHelper.Lerp(selectedTargetingParams.CircleMaxRandomOffset, 0, currentAttackIntensity * Rand.Range(0.9f, 1.1f))); canAttack = false; aggressionIntensity = Math.Clamp(aggressionIntensity, AIParams.StartAggression, AIParams.MaxAggression); - CirclePhase = Vector2.DistanceSquared(WorldPosition, attackWorldPos) > MathUtils.Pow2(circleFallbackDistance) ? CirclePhase.CloseIn : CirclePhase.FallBack; + if (targetSub.Borders.Width < 1000) + { + breakCircling = true; + CirclePhase = CirclePhase.CloseIn; + } + else if (sqrDistToSub > MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance)) + { + CirclePhase = CirclePhase.CloseIn; + } + else if (sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance)) + { + CirclePhase = CirclePhase.FallBack; + } + else + { + CirclePhase = CirclePhase.Advance; + } break; case CirclePhase.CloseIn: - var sub = SelectedAiTarget.Entity?.Submarine; - if (sub == null) - { - CirclePhase = CirclePhase.Start; - break; - } - if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * GetStrikeDistanceMultiplier(sub.Velocity)) + if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * GetStrikeDistanceMultiplier(targetSub.Velocity)) { strikeTimer = AttackingLimb.attack.CoolDown; CirclePhase = CirclePhase.Strike; } - else if (!breakCircling && Vector2.DistanceSquared(WorldPosition, attackWorldPos) <= MathUtils.Pow2(circleFallbackDistance - 1000) && - sub.Velocity.LengthSquared() <= MathUtils.Pow2(GetTargetMaxSpeed())) + else if (!breakCircling && sqrDistToSub <= MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance / 2) && targetSub.Velocity.LengthSquared() <= MathUtils.Pow2(GetTargetMaxSpeed())) { CirclePhase = CirclePhase.Advance; } @@ -1494,23 +1508,17 @@ namespace Barotrauma break; case CirclePhase.FallBack: bool isBlocked = !UpdateFallBack(attackWorldPos, deltaTime, followThrough: false, checkBlocking: true); - if (isBlocked || Vector2.DistanceSquared(WorldPosition, attackWorldPos) > MathUtils.Pow2(circleFallbackDistance)) + if (isBlocked || sqrDistToSub > MathUtils.Pow2(subSize + circleFallbackDistance)) { CirclePhase = CirclePhase.Advance; break; } return; case CirclePhase.Advance: - var targetSub = SelectedAiTarget.Entity?.Submarine; - if (targetSub == null) - { - CirclePhase = CirclePhase.Start; - break; - } Vector2 subSpeed = targetSub.Velocity; float requiredDistMultiplier = 1; // If the target sub is moving fast, just steer towards the target until close enough to strike - if (breakCircling || subSpeed.LengthSquared() > MathUtils.Pow2(GetTargetMaxSpeed()) || distance > selectedTargetingParams.CircleStartDistance + 1000) + if (breakCircling || subSpeed.LengthSquared() > MathUtils.Pow2(GetTargetMaxSpeed()) || sqrDistToSub > MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance * 1.2f)) { CirclePhase = CirclePhase.CloseIn; } @@ -1532,7 +1540,7 @@ namespace Barotrauma // When the offset position is outside of the sub it happens that the creature sometimes reaches the target point, // which makes it continue circling around the point (as supposed) // But when there is some offset and the offset is too near, this is not what we want. - if (targetSub.Borders.ContainsWorld(attackWorldPos + ConvertUnits.ToDisplayUnits(circleOffset))) + if (AttackingLimb != null && sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance)) { CirclePhase = CirclePhase.Strike; strikeTimer = AttackingLimb.attack.CoolDown; @@ -1646,15 +1654,15 @@ namespace Barotrauma return; } } - if (UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb)) - { - CirclePhase = CirclePhase.Start; - } - else + if (!UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb)) { IgnoreTarget(SelectedAiTarget); } } + else if (IsAttackRunning) + { + AttackingLimb.attack.ResetAttackTimer(); + } } private readonly List attackLimbs = new List(); @@ -2060,6 +2068,7 @@ namespace Barotrauma targetValue = 0; selectedTargetMemory = null; targetingParams = null; + bool isAnyTargetClose = false; foreach (AITarget aiTarget in AITarget.List) { @@ -2163,6 +2172,14 @@ namespace Barotrauma continue; } } + if (door == null) + { + // Ignore items inside ruins, unless we are in the same hull. We can't target the ruin walls. + if (item.Submarine == null && item.CurrentHull != Character.CurrentHull) + { + continue; + } + } foreach (var prio in AIParams.Targets) { if (item.HasTag(prio.Tag)) @@ -2379,18 +2396,16 @@ namespace Barotrauma } } } - + if (!aiTarget.IsWithinSector(WorldPosition)) { continue; } Vector2 toTarget = aiTarget.WorldPosition - character.WorldPosition; float dist = toTarget.Length(); - + float nonModifiedDist = dist; //if the target has been within range earlier, the character will notice it more easily if (targetMemories.ContainsKey(aiTarget)) { dist *= 0.9f; } - if (!CanPerceive(aiTarget, dist)) { continue; } - if (!aiTarget.IsWithinSector(WorldPosition)) { continue; } //if the target is very close, the distance doesn't make much difference // -> just ignore the distance and attack whatever has the highest priority @@ -2405,14 +2420,26 @@ namespace Barotrauma if (targetParams.AttackPattern == AttackPattern.Circle) { - if (Character.Submarine == null && aiTarget.Entity?.Submarine != null) + if (Character.Submarine == null && aiTarget.Entity?.Submarine != null && !isAnyTargetClose) { if (Submarine.MainSubs.Contains(aiTarget.Entity.Submarine)) { - // Prioritize targets that are near the horizontal center of the sub + // Prioritize targets that are near the horizontal center of the sub, but only when none of the targets is reachable. float horizontalDistanceToSubCenter = Math.Abs(aiTarget.WorldPosition.X - aiTarget.Entity.Submarine.WorldPosition.X); dist *= MathHelper.Lerp(1f, 5f, MathUtils.InverseLerp(0, 10000, horizontalDistanceToSubCenter)); } + else + { + dist *= 5; + } + } + } + // Don't target characters that are outside of the allowed zone, unless attacking or escaping + if (targetParams.State != AIState.Attack && targetParams.State != AIState.Escape && targetParams.State != AIState.Avoid) + { + if (!IsPositionInsideAllowedZone(aiTarget.WorldPosition, out _)) + { + continue; } } @@ -2486,11 +2513,26 @@ namespace Barotrauma } } } + else if (targetCharacter.Submarine == null && Character.Submarine == null) + { + // Ignore the target when it's far enough and blocked by the level geometry, because the steering avoidance probably can't get us to the target. + if (dist > Math.Clamp(ConvertUnits.ToDisplayUnits(colliderLength) * 10, 1000, 5000)) + { + if (Submarine.PickBodies(SimPosition, targetCharacter.SimPosition, collisionCategory: Physics.CollisionLevel).Any()) + { + continue; + } + } + } } newTarget = aiTarget; selectedTargetMemory = targetMemory; targetValue = valueModifier; targetingParams = targetParams; + if (!isAnyTargetClose) + { + isAnyTargetClose = ConvertUnits.ToDisplayUnits(colliderLength) > nonModifiedDist; + } } } @@ -2619,7 +2661,7 @@ namespace Barotrauma } } } - if (!Character.AnimController.CanEnterSubmarine && wallTarget == null) + if (!Character.AnimController.CanEnterSubmarine && wallTarget == null && selectedTargetingParams?.AttackPattern == AttackPattern.Straight) { if (closestBody.UserData is Structure w && w.Submarine != null && w.Submarine == SelectedAiTarget.Entity?.Submarine || closestBody.UserData is Item i && i.Submarine != null && i.Submarine == SelectedAiTarget.Entity?.Submarine) @@ -2753,6 +2795,44 @@ namespace Barotrauma private readonly float stateResetCooldown = 10; private float stateResetTimer; private bool isStateChanged; + private readonly Dictionary activeTriggers = new Dictionary(); + private readonly HashSet inactiveTriggers = new HashSet(); + + public void LaunchTrigger(AITrigger trigger) + { + if (trigger.IsTriggered) { return; } + if (activeTriggers.ContainsKey(trigger)) { return; } + if (activeTriggers.ContainsValue(selectedTargetingParams)) + { + if (!trigger.AllowToOverride) { return; } + var existingTrigger = activeTriggers.FirstOrDefault(kvp => kvp.Value == selectedTargetingParams && kvp.Key.AllowToBeOverridden); + if (existingTrigger.Key == null) { return; } + activeTriggers.Remove(existingTrigger.Key); + } + trigger.Launch(); + activeTriggers.Add(trigger, selectedTargetingParams); + ChangeParams(selectedTargetingParams, trigger.State); + } + + private void UpdateTriggers(float deltaTime) + { + foreach (var triggerObject in activeTriggers) + { + AITrigger trigger = triggerObject.Key; + trigger.UpdateTimer(deltaTime); + if (!trigger.IsActive) + { + trigger.Reset(); + ResetParams(triggerObject.Value); + inactiveTriggers.Add(trigger); + } + } + foreach (AITrigger trigger in inactiveTriggers) + { + activeTriggers.Remove(trigger); + } + inactiveTriggers.Clear(); + } /// /// Resets the target's state to the original value defined in the xml. @@ -2768,11 +2848,7 @@ namespace Barotrauma tempParams.Values.ForEach(t => AIParams.RemoveTarget(t)); tempParams.Remove(tag); } - targetParams.Reset(); - ResetAITarget(); - // Enforce the idle state so that we don't keep following the target if there's one - State = AIState.Idle; - PreviousState = AIState.Idle; + ResetParams(targetParams); return true; } else @@ -2784,6 +2860,27 @@ namespace Barotrauma private readonly Dictionary modifiedParams = new Dictionary(); private readonly Dictionary tempParams = new Dictionary(); + private void ChangeParams(CharacterParams.TargetParams targetParams, AIState state, float? priority = null) + { + if (targetParams == null) { return; } + if (priority.HasValue) + { + targetParams.Priority = priority.Value; + } + targetParams.State = state; + } + + private void ResetParams(CharacterParams.TargetParams targetParams) + { + targetParams?.Reset(); + if (selectedTargetingParams == targetParams || State == AIState.Idle) + { + ResetAITarget(); + State = AIState.Idle; + PreviousState = AIState.Idle; + } + } + private void ChangeParams(string tag, AIState state, float? priority = null, bool onlyExisting = false) { if (!AIParams.TryGetTarget(tag, out CharacterParams.TargetParams targetParams)) @@ -2938,45 +3035,56 @@ namespace Barotrauma } } + private bool IsPositionInsideAllowedZone(Vector2 pos, out Vector2 targetDir) + { + targetDir = Vector2.Zero; + if (AIParams.AvoidAbyss) + { + if (pos.Y < Level.Loaded.AbyssStart) + { + // Too far down + targetDir = Vector2.UnitY; + } + } + if (AIParams.StayInAbyss) + { + if (pos.Y > Level.Loaded.AbyssStart) + { + // Too far up + targetDir = -Vector2.UnitY; + } + else if (pos.Y < Level.Loaded.AbyssEnd) + { + // Too far down + targetDir = Vector2.UnitY; + } + } + float margin = 30000; + if (pos.X < -margin) + { + // Too far left + targetDir = Vector2.UnitX; + } + else if (pos.X > Level.Loaded.Size.X + margin) + { + // Too far right + targetDir = -Vector2.UnitX; + } + return targetDir == Vector2.Zero; + } + private Vector2 returnDir; private float returnTimer; private void SteerInsideLevel(float deltaTime) { - if (State == AIState.Attack) { return; } if (SteeringManager is IndoorsSteeringManager) { return; } if (Level.Loaded == null) { return; } - Point levelSize = Level.Loaded.Size; - float returnTime = 10; - if (AIParams.AvoidAbyss) + if (State == AIState.Attack && returnTimer <= 0) { return; } + float returnTime = 5; + if (!IsPositionInsideAllowedZone(WorldPosition, out Vector2 targetDir)) { - if (WorldPosition.Y < Level.Loaded.AbyssStart) - { - // Too far down - returnTimer = returnTime * Rand.Range(0.75f, 1.25f); - returnDir = Vector2.UnitY; - } - } - else if (AIParams.StayInAbyss) - { - if (WorldPosition.Y > Level.Loaded.AbyssStart) - { - // Too far up - returnTimer = returnTime * Rand.Range(0.75f, 1.25f); - returnDir = -Vector2.UnitY; - } - } - float margin = AIParams.AvoidAbyss ? 0 : 30000; - if (WorldPosition.X < margin) - { - // Too far left + returnDir = targetDir; returnTimer = returnTime * Rand.Range(0.75f, 1.25f); - returnDir = Vector2.UnitX; - } - if (WorldPosition.X > levelSize.X + margin) - { - // Too far right - returnTimer = returnTime * Rand.Range(0.75f, 1.25f); - returnDir = -Vector2.UnitX; } if (returnTimer > 0) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index dade0434f..4d95e9afc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -15,7 +15,7 @@ namespace Barotrauma private readonly AIObjectiveManager objectiveManager; - private float sortTimer; + public float SortTimer { get; set; } private float crouchRaycastTimer; private float reactTimer; private float unreachableClearTimer; @@ -131,7 +131,7 @@ namespace Barotrauma outsideSteering = new SteeringManager(this); objectiveManager = new AIObjectiveManager(c); reactTimer = GetReactionTime(); - sortTimer = Rand.Range(0f, sortObjectiveInterval); + SortTimer = Rand.Range(0f, sortObjectiveInterval); } public override void Update(float deltaTime) @@ -218,6 +218,7 @@ namespace Barotrauma foreach (Character c in Character.CharacterList) { if (c.Submarine != Character.Submarine) { continue; } + if (c.Removed || c.IsDead || c.IsIncapacitated) { continue; } if (IsFriendly(c)) { continue; } Vector2 toTarget = c.WorldPosition - WorldPosition; float dist = toTarget.LengthSquared(); @@ -264,14 +265,14 @@ namespace Barotrauma CheckCrouching(deltaTime); Character.ClearInputs(); - if (sortTimer > 0.0f) + if (SortTimer > 0.0f) { - sortTimer -= deltaTime; + SortTimer -= deltaTime; } else { objectiveManager.SortObjectives(); - sortTimer = sortObjectiveInterval; + SortTimer = sortObjectiveInterval; } objectiveManager.UpdateObjectives(deltaTime); @@ -288,14 +289,14 @@ namespace Barotrauma { if (Character.CurrentHull != null) { - if (Character.TeamID == CharacterTeamType.FriendlyNPC) + if (Character.IsOnPlayerTeam) { - // Outpost npcs don't inform each other about threats, like crew members do. - VisibleHulls.ForEach(h => RefreshHullSafety(h)); + VisibleHulls.ForEach(h => PropagateHullSafety(Character, h)); } else { - VisibleHulls.ForEach(h => PropagateHullSafety(Character, h)); + // Outpost npcs don't inform each other about threats, like crew members do. + VisibleHulls.ForEach(h => RefreshHullSafety(h)); } } if (Character.SpeechImpediment < 100.0f) @@ -1065,7 +1066,9 @@ namespace Barotrauma { if (!IsFriendly(attacker)) { - return c.IsSecurity ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive; + return c.AIController is HumanAIController humanAI && + (humanAI.ObjectiveManager.IsCurrentOrder() || humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders)) + ? AIObjectiveCombat.CombatMode.Offensive : AIObjectiveCombat.CombatMode.Defensive; } else { @@ -1192,7 +1195,7 @@ namespace Barotrauma { base.Reset(); objectiveManager.SortObjectives(); - sortTimer = sortObjectiveInterval; + SortTimer = sortObjectiveInterval; float waitDuration = characterWaitOnSwitch; if (ObjectiveManager.IsCurrentObjective()) { @@ -1418,6 +1421,9 @@ namespace Barotrauma item.StolenDuringRound = true; otherCharacter.Speak(TextManager.Get("dialogstealwarning"), null, Rand.Range(0.5f, 1.0f), "thief", 10.0f); someoneSpoke = true; +#if CLIENT + HintManager.OnStoleItem(thief, item); +#endif } // React if we are security if (!TriggerSecurity(otherHumanAI)) @@ -1554,7 +1560,7 @@ namespace Barotrauma targetAdded = true; } } - }, (caller.AIController as HumanAIController)?.ReportRange ?? float.PositiveInfinity); + }, range: (caller.AIController as HumanAIController)?.ReportRange ?? float.PositiveInfinity); return targetAdded; } @@ -1726,11 +1732,9 @@ namespace Barotrauma switch (myTeam) { case CharacterTeamType.None: - // Only enemies are in the Team "None" - return false; case CharacterTeamType.Team1: case CharacterTeamType.Team2: - // Team1 is only friendly to Team1 and friendly NPCs + // Only friendly to the same team and friendly NPCs return otherTeam == CharacterTeamType.FriendlyNPC; case CharacterTeamType.FriendlyNPC: // Friendly NPCs are friendly to both teams diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/NPCConversation.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/NPCConversation.cs index 9a822be02..8cd4fb016 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/NPCConversation.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/NPCConversation.cs @@ -221,6 +221,19 @@ namespace Barotrauma { currentFlags.Add("CampaignNPC." + speaker.CampaignInteractionType); } + + if (GameMain.GameSession?.GameMode is CampaignMode campaignMode && + (campaignMode.Map?.CurrentLocation?.Type?.Identifier.Equals("abandoned", StringComparison.OrdinalIgnoreCase) ?? false)) + { + if (speaker.TeamID == CharacterTeamType.None) + { + currentFlags.Add("Bandit"); + } + else if (speaker.TeamID == CharacterTeamType.FriendlyNPC) + { + currentFlags.Add("Hostage"); + } + } } return currentFlags; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs index a1a9b6999..f2066e541 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -79,6 +79,7 @@ namespace Barotrauma private float coolDownTimer; private IEnumerable myBodies; private float aimTimer; + private float reloadTimer; private float spreadTimer; private bool canSeeTarget; @@ -147,6 +148,7 @@ namespace Barotrauma Mode = CombatMode.Retreat; } spreadTimer = Rand.Range(-10, 10); + HumanAIController.SortTimer = 0; } public override float GetPriority() @@ -170,6 +172,10 @@ namespace Barotrauma base.Update(deltaTime); ignoreWeaponTimer -= deltaTime; checkWeaponsTimer -= deltaTime; + if (reloadTimer > 0) + { + reloadTimer -= deltaTime; + } if (ignoreWeaponTimer < 0) { ignoredWeapons.Clear(); @@ -219,7 +225,11 @@ namespace Barotrauma { OperateWeapon(deltaTime); } - if (!HoldPosition && seekAmmunitionObjective == null && seekWeaponObjective == null) + if (HoldPosition) + { + SteeringManager.Reset(); + } + else if (seekAmmunitionObjective == null && seekWeaponObjective == null) { Move(deltaTime); } @@ -641,7 +651,7 @@ namespace Barotrauma var slots = Weapon.AllowedSlots.Where(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand)); if (character.Inventory.TryPutItem(Weapon, character, slots)) { - aimTimer = Rand.Range(1f, 1.5f) / AimSpeed; + aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed; } else { @@ -912,7 +922,7 @@ namespace Barotrauma } if (!canSeeTarget) { - aimTimer = Rand.Range(0.2f, 1f) / AimSpeed; + aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed; return; } if (Weapon.RequireAimToUse) @@ -930,6 +940,7 @@ namespace Barotrauma aimTimer -= deltaTime; return; } + if (reloadTimer > 0) { return; } if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 1) { return; } if (holdFireCondition != null && holdFireCondition()) { return; } float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position); @@ -1010,18 +1021,25 @@ namespace Barotrauma private void UseWeapon(float deltaTime) { - character.SetInput(InputType.Shoot, false, true); - Weapon.Use(deltaTime, character); float reloadTime = 0; if (WeaponComponent is RangedWeapon rangedWeapon) { - reloadTime = rangedWeapon.Reload; + // If the weapon is just equipped, we can't shoot just yet. + if (rangedWeapon.ReloadTimer <= 0) + { + reloadTime = rangedWeapon.Reload; + } } if (WeaponComponent is MeleeWeapon mw) { - reloadTime = mw.Reload; + if (!((HumanoidAnimController)character.AnimController).Crouching) + { + reloadTime = mw.Reload; + } } - aimTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.5f) / AimSpeed); + character.SetInput(InputType.Shoot, false, true); + Weapon.Use(deltaTime, character); + reloadTimer = Math.Max(reloadTime, reloadTime * Rand.Range(1f, 1.25f) / AimSpeed); } protected override void OnCompleted() @@ -1031,10 +1049,7 @@ namespace Barotrauma { Unequip(); } - if (!HoldPosition) - { - SteeringManager.Reset(); - } + SteeringManager.Reset(); } protected override void OnAbandon() @@ -1044,10 +1059,7 @@ namespace Barotrauma { Unequip(); } - if (!HoldPosition) - { - SteeringManager.Reset(); - } + SteeringManager.Reset(); } public override void Reset() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFightIntruders.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFightIntruders.cs index 1c4e86800..8187e6092 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFightIntruders.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFightIntruders.cs @@ -10,6 +10,8 @@ namespace Barotrauma protected override float IgnoreListClearInterval => 30; public override bool IgnoreUnsafeHulls => true; + protected override float TargetUpdateTimeMultiplier => 0.2f; + public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } @@ -48,7 +50,8 @@ namespace Barotrauma public static bool IsValidTarget(Character target, Character character) { - if (target == null || target.IsDead || target.Removed) { return false; } + if (target == null || target.Removed) { return false; } + if (target.IsDead || target.IsUnconscious) { return false; } if (target == character) { return false; } if (target.Submarine == null) { return false; } if (character.Submarine == null) { return false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs index 5b7be664b..fd2571d9e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -555,6 +555,13 @@ namespace Barotrauma //otherwise characters can let go of the ladders too soon once they're close enough to the target if (PathSteering.CurrentPath.NextNode != null) { return false; } } + if (!character.AnimController.InWater) + { + float yDiff = Math.Abs(Target.WorldPosition.Y - character.WorldPosition.Y); + if (yDiff > CloseEnough) { return false; } + float xDiff = Math.Abs(Target.WorldPosition.X - character.WorldPosition.X); + return xDiff <= CloseEnough; + } return Vector2.DistanceSquared(Target.WorldPosition, character.WorldPosition) < CloseEnough * CloseEnough; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveLoop.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveLoop.cs index a710e81c9..9635b79d2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveLoop.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveLoop.cs @@ -11,6 +11,7 @@ namespace Barotrauma protected HashSet ignoreList = new HashSet(); private float ignoreListTimer; protected float targetUpdateTimer; + protected virtual float TargetUpdateTimeMultiplier { get; } = 1; private float syncTimer; private readonly float syncTime = 1; @@ -61,7 +62,7 @@ namespace Barotrauma ignoreListTimer += deltaTime; } } - if (targetUpdateTimer < 0) + if (targetUpdateTimer <= 0) { UpdateTargets(); } @@ -69,9 +70,9 @@ namespace Barotrauma { targetUpdateTimer -= deltaTime; } - if (syncTimer < 0) + if (syncTimer <= 0) { - syncTimer = syncTime * Rand.Range(0.9f, 1.1f); + syncTimer = Math.Min(syncTime * Rand.Range(0.9f, 1.1f), targetUpdateTimer); // Sync objectives, subobjectives and targets foreach (var objective in Objectives) { @@ -95,7 +96,7 @@ namespace Barotrauma } // the timer is set between 1 and 10 seconds, depending on the priority modifier and a random +-25% - private float SetTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1); + private float CalculateTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1) * TargetUpdateTimeMultiplier; public override void Reset() { @@ -156,7 +157,7 @@ namespace Barotrauma protected void UpdateTargets() { - SetTargetUpdateTimer(); + CalculateTargetUpdateTimer(); Targets.Clear(); FindTargets(); CreateObjectives(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs index 23010dc8c..a433d8b52 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -386,7 +386,9 @@ namespace Barotrauma Abandon = true; return false; } - bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter); + bool isCompleted = + AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) || + targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength < a.Prefab.TreatmentThreshold); if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name), diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs index 9cef0c85e..2fffa665f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs @@ -25,8 +25,8 @@ namespace Barotrauma { // When targeting player characters, always treat them when ordered, else use the threshold so that minor/non-severe damage is ignored. // If we ignore any damage when the player orders a bot to do healings, it's observed to cause confusion among the players. - // On the other hand, if the bots too eagerly heal characters when it's not nevessary, it's inefficient and can feel frustrating, because it can't be controlled. - return character == target || manager.CurrentOrder is AIObjectiveRescueAll ? (target.IsPlayer ? 100 : vitalityThresholdForOrders) : vitalityThreshold; + // On the other hand, if the bots too eagerly heal characters when it's not necessary, it's inefficient and can feel frustrating, because it can't be controlled. + return character == target || manager.HasOrder() ? (target.IsPlayer ? 100 : vitalityThresholdForOrders) : vitalityThreshold; } } @@ -83,7 +83,7 @@ namespace Barotrauma if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target)) { return false; } - if (!humanAI.ObjectiveManager.IsCurrentOrder()) + if (!humanAI.ObjectiveManager.HasOrder()) { if (!character.IsMedic && target != character) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs index f3049f3e7..eae42212a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/Ragdoll.cs @@ -281,7 +281,7 @@ namespace Barotrauma } } - public const float MAX_SPEED = 30; + public const float MAX_SPEED = 20; public Vector2 TargetMovement { @@ -636,9 +636,12 @@ namespace Barotrauma //always collides with bodies other than structures if (!(f2.Body.UserData is Structure structure)) { - lock (impactQueue) + if (!f2.IsSensor) { - impactQueue.Enqueue(new Impact(f1, f2, contact, velocity)); + lock (impactQueue) + { + impactQueue.Enqueue(new Impact(f1, f2, contact, velocity)); + } } return true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index f05e16ba6..02f1d40c3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -156,6 +156,8 @@ namespace Barotrauma public Entity LastDamageSource; + public AttackResult LastDamage; + public float InvisibleTimer; private CharacterPrefab prefab; @@ -199,7 +201,12 @@ namespace Barotrauma set => Params.Visibility = value; } - public bool IsTraitor; + public bool IsTraitor + { + get; + set; + } + public string TraitorCurrentObjective = ""; public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase); public bool IsMale => Info != null && Info.HasGenders && Info.Gender == Gender.Male; @@ -333,6 +340,7 @@ namespace Barotrauma //text displayed when the character is highlighted if custom interact is set public string customInteractHUDText; private Action onCustomInteract; + public ConversationAction ActiveConversation; public bool AllowCustomInteract { @@ -349,6 +357,9 @@ namespace Barotrauma set { lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f); +#if CLIENT + HintManager.OnHandcuffed(this); +#endif } } @@ -605,6 +616,9 @@ namespace Barotrauma get => _selectedConstruction; set { +#if CLIENT + HintManager.OnSetSelectedConstruction(this, _selectedConstruction, value); +#endif _selectedConstruction = value; #if CLIENT if (Controlled == this) @@ -1661,6 +1675,12 @@ namespace Barotrauma { item.Use(deltaTime, this); } +#if CLIENT + else if (item.RequireAimToUse && !IsKeyDown(InputType.Aim)) + { + HintManager.OnShootWithoutAiming(this, item); + } +#endif } } } @@ -1853,6 +1873,19 @@ namespace Barotrauma return false; } + public Item GetEquippedItem(string tagOrIdentifier) + { + if (Inventory == null) { return null; } + for (int i = 0; i < Inventory.Capacity; i++) + { + if (Inventory.SlotTypes[i] == InvSlotType.Any) { continue; } + var item = Inventory.GetItemAt(i); + if (item == null) { continue; } + if (item.Prefab.Identifier == tagOrIdentifier || item.HasTag(tagOrIdentifier)) { return item; } + } + return null; + } + public bool CanAccessInventory(Inventory inventory) { if (!CanInteract || inventory.Locked) { return false; } @@ -2857,6 +2890,7 @@ namespace Barotrauma { if (character == this) { continue; } if (character.TeamID != TeamID) { continue; } + if (!HumanAIController.IsActive(character)) { continue; } foreach (var currentOrder in character.CurrentOrders) { if (currentOrder.Order == null) { continue; } @@ -3268,12 +3302,13 @@ namespace Barotrauma //#endif // } + SetStun(stun); + if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire) { if (attacker.TeamID == TeamID) { return new AttackResult(); } } - SetStun(stun); Vector2 dir = hitLimb.WorldPosition - worldPosition; if (Math.Abs(attackImpulse) > 0.0f) { @@ -3308,6 +3343,7 @@ namespace Barotrauma }; if (attackResult.Damage > 0) { + LastDamage = attackResult; ApplyStatusEffects(ActionType.OnDamaged, 1.0f); hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f); if (attacker != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs index be7848329..818bcdd8b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/CharacterInfo.cs @@ -153,6 +153,8 @@ namespace Barotrauma private static ushort idCounter; private const string disguiseName = "???"; + public bool HasNickname => Name != OriginalName; + public string OriginalName { get; private set; } public string Name; public string DisplayName { @@ -453,7 +455,7 @@ namespace Barotrauma public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1; // Used for creating the data - public CharacterInfo(string speciesName, string name = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced) + public CharacterInfo(string speciesName, string name = "", string originalName = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced) { if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) { @@ -503,6 +505,7 @@ namespace Barotrauma } } } + OriginalName = !string.IsNullOrEmpty(originalName) ? originalName : Name; personalityTrait = NPCPersonalityTrait.GetRandom(name + HeadSpriteId); Salary = CalculateSalary(); if (ragdollFileName != null) @@ -518,6 +521,7 @@ namespace Barotrauma ID = idCounter; idCounter++; Name = infoElement.GetAttributeString("name", ""); + OriginalName = infoElement.GetAttributeString("originalname", null); string genderStr = infoElement.GetAttributeString("gender", "male").ToLowerInvariant(); Salary = infoElement.GetAttributeInt("salary", 1000); Enum.TryParse(infoElement.GetAttributeString("race", "White"), true, out Race race); @@ -576,6 +580,11 @@ namespace Barotrauma } } + if (string.IsNullOrEmpty(OriginalName)) + { + OriginalName = Name; + } + StartItemsGiven = infoElement.GetAttributeBool("startitemsgiven", false); string personalityName = infoElement.GetAttributeString("personality", ""); ragdollFileName = infoElement.GetAttributeString("ragdoll", string.Empty); @@ -622,7 +631,17 @@ namespace Barotrauma public int GetIdentifier() { - int id = ToolBox.StringToInt(Name); + return GetIdentifier(Name); + } + + public int GetIdentifierUsingOriginalName() + { + return GetIdentifier(OriginalName); + } + + private int GetIdentifier(string name) + { + int id = ToolBox.StringToInt(name); id ^= HeadSpriteId; id ^= (int)Race << 6; id ^= HairIndex << 12; @@ -939,12 +958,24 @@ namespace Barotrauma partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos); + public void Rename(string newName) + { + if (string.IsNullOrEmpty(newName)) { return; } + Name = newName; + } + + public void ResetName() + { + Name = OriginalName; + } + public XElement Save(XElement parentElement) { XElement charElement = new XElement("Character"); charElement.Add( new XAttribute("name", Name), + new XAttribute("originalname", OriginalName), new XAttribute("speciesname", SpeciesName), new XAttribute("gender", Head.gender == Gender.Male ? "male" : "female"), new XAttribute("race", Head.race.ToString()), @@ -957,7 +988,7 @@ namespace Barotrauma new XAttribute("startitemsgiven", StartItemsGiven), new XAttribute("ragdoll", ragdollFileName), new XAttribute("personality", personalityTrait == null ? "" : personalityTrait.Name)); - + // TODO: animations? if (Character != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs index 62fe02062..dacb9e3f2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs @@ -260,10 +260,13 @@ namespace Barotrauma /// /// Use this method to skip clamping and additional logic of the setters. - /// Intended only to be used when the value is already clamped! (networking code) /// Ideally we would keep this private, but doing so would require too much refactoring. /// - public void SetStrength(float strength) => _strength = strength; + public void SetStrength(float strength) + { + _nonClampedStrength = strength; + _strength = _nonClampedStrength; + } public bool ShouldShowIcon(Character afflictedCharacter) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs index 117a3c108..a9c953b85 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs @@ -290,6 +290,9 @@ namespace Barotrauma //how high the strength has to be for the affliction icon to be shown with a health scanner public readonly float ShowInHealthScannerThreshold = 0.05f; + //how strong the affliction needs to be before bots attempt to treat it + public readonly float TreatmentThreshold = 5.0f; + //how much karma changes when a player applies this affliction to someone (per strength of the affliction) public float KarmaChangeOnApplied; @@ -376,6 +379,9 @@ namespace Barotrauma { DebugConsole.ThrowError("Cannot override all afflictions, because many of them are required by the main game! Please try overriding them one by one."); } + + List<(AfflictionPrefab prefab, XElement element)> loadedAfflictions = new List<(AfflictionPrefab prefab, XElement element)>(); + foreach (XElement element in mainElement.Elements()) { bool isOverride = element.IsOverride(); @@ -510,10 +516,18 @@ namespace Barotrauma if (prefab != null) { + loadedAfflictions.Add((prefab, element)); Prefabs.Add(prefab, isOverride); prefab.CalculatePrefabUIntIdentifier(Prefabs); } } + + //load the effects after all the afflictions in the file have been instantiated + //otherwise afflictions can't inflict other afflictions that are defined at a later point in the file + foreach ((AfflictionPrefab prefab, XElement element) in loadedAfflictions) + { + prefab.LoadEffects(element); + } } public static void RemoveByFile(string filePath) @@ -565,6 +579,7 @@ namespace Barotrauma MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f); ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f)); + TreatmentThreshold = element.GetAttributeFloat("treatmentthreshold", Math.Max(ActivationThreshold, 5.0f)); DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f); BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f); @@ -584,9 +599,6 @@ namespace Barotrauma case "icon": Icon = new Sprite(subElement); break; - case "effect": - effects.Add(new Effect(subElement, Name)); - break; case "periodiceffect": periodicEffects.Add(new PeriodicEffect(subElement, Name)); break; @@ -614,6 +626,19 @@ namespace Barotrauma constructor = type.GetConstructor(new[] { typeof(AfflictionPrefab), typeof(float) }); } + private void LoadEffects(XElement element) + { + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLowerInvariant()) + { + case "effect": + effects.Add(new Effect(subElement, Name)); + break; + } + } + } + public override string ToString() { return "AfflictionPrefab (" + Name + ")"; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs index f94e72bb5..02d4da315 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs @@ -36,7 +36,11 @@ namespace Barotrauma public LimbHealth(XElement element, CharacterHealth characterHealth) { - Name = TextManager.Get("HealthLimbName." + element.GetAttributeString("name", "")); + string limbName = element.GetAttributeString("name", null) ?? "generic"; + if (limbName != "generic") + { + Name = TextManager.Get("HealthLimbName." + limbName); + } this.characterHealth = characterHealth; foreach (XElement subElement in element.Elements()) { @@ -664,7 +668,6 @@ namespace Barotrauma } } - partial void UpdateProjSpecific(float deltaTime); partial void UpdateLimbAfflictionOverlays(); @@ -687,6 +690,10 @@ namespace Barotrauma { var affliction = limbHealths[i].Afflictions[j]; Limb targetLimb = Character.AnimController.Limbs.LastOrDefault(l => !l.IsSevered && !l.Hidden && l.HealthIndex == i); + if (targetLimb == null) + { + targetLimb = Character.AnimController.MainLimb; + } affliction.Update(this, targetLimb, deltaTime); affliction.DamagePerSecondTimer += deltaTime; if (affliction is AfflictionBleeding bleeding) @@ -877,6 +884,7 @@ namespace Barotrauma float minSuitability = -10, maxSuitability = 10; foreach (Affliction affliction in GetAllAfflictions()) { + if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; } foreach (KeyValuePair treatment in affliction.Prefab.TreatmentSuitability) { if (!treatmentSuitability.ContainsKey(treatment.Key)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs index f2928903f..c95a4feb2 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/HumanPrefab.cs @@ -20,6 +20,9 @@ namespace Barotrauma [Serialize(1f, false)] public float HealthMultiplier { get; protected set; } + [Serialize(1f, false)] + public float HealthMultiplierInMultiplayer { get; protected set; } + [Serialize(1f, false)] public float AimSpeed { get; protected set; } @@ -117,6 +120,10 @@ namespace Barotrauma public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null) { npc.CharacterHealth.MaxVitality *= HealthMultiplier; + if (GameMain.NetworkMember != null) + { + npc.CharacterHealth.MaxVitality *= HealthMultiplierInMultiplayer; + } var humanAI = npc.AIController as HumanAIController; if (humanAI != null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs index 72c053579..e08596e97 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Limb.cs @@ -732,6 +732,10 @@ namespace Barotrauma { newAffliction = affliction.CreateMultiplied(finalDamageModifier); } + else + { + newAffliction.SetStrength(affliction.NonClampedStrength); + } if (applyAffliction) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs index efde3633b..13a7c02d8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs @@ -430,10 +430,10 @@ namespace Barotrauma [Serialize(false, true)] public bool UseHealthWindow { get; set; } - [Serialize(0f, true, description: "How easily the character heals from the bleeding wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)] + [Serialize(0f, true, description: "How easily the character heals from the bleeding wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)] public float BleedingReduction { get; private set; } - [Serialize(0f, true, description: "How easily the character heals from the burn wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)] + [Serialize(0f, true, description: "How easily the character heals from the burn wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)] public float BurnReduction { get; private set; } [Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)] diff --git a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs index 8ce405380..fac630062 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/DebugConsole.cs @@ -707,9 +707,10 @@ namespace Barotrauma commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) => { +#if CLIENT + if (Screen.Selected == GameMain.SubEditorScreen) { return; } Character.Controlled = null; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; -#if CLIENT GameMain.Client?.SendConsoleCommand("freecam"); #endif }, isCheat: true)); @@ -1695,6 +1696,8 @@ namespace Barotrauma return null; } + // Use same sorting as DebugConsole.ListCharacterNames() above + matchingCharacters = matchingCharacters.OrderBy(c => c.IsDead).ThenByDescending(c => c.IsHuman).ToList(); if (characterIndex == -1) { if (matchingCharacters.Count > 1) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/ConversationAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/ConversationAction.cs index daa5e8fd0..dd213330b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/ConversationAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/ConversationAction.cs @@ -179,6 +179,7 @@ namespace Barotrauma { if (speaker == null) { return; } speaker.CampaignInteractionType = CampaignMode.InteractionType.None; + speaker.ActiveConversation = this; speaker.SetCustomInteract(null, null); #if SERVER GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction }); @@ -213,24 +214,24 @@ namespace Barotrauma #if CLIENT Character.DisableControls = true; #endif - if (ShouldInterrupt()) + if (ShouldInterrupt()) { ResetSpeaker(); - interrupt = true; + interrupt = true; } - return; + return; } if (!string.IsNullOrEmpty(SpeakerTag)) { - if (speaker != null && !speaker.Removed && speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk) { return; } + if (speaker != null && !speaker.Removed && speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk && speaker.ActiveConversation?.ParentEvent != this.ParentEvent) { return; } speaker = ParentEvent.GetTargets(SpeakerTag).FirstOrDefault(e => e is Character) as Character; if (speaker == null || speaker.Removed) - { - return; + { + return; } //some conversation already assigned to the speaker, wait for it to be removed - if (speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk) + if (speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk && speaker.ActiveConversation?.ParentEvent != this.ParentEvent) { return; } @@ -241,6 +242,7 @@ namespace Barotrauma else { speaker.CampaignInteractionType = CampaignMode.InteractionType.Talk; + speaker.ActiveConversation = this; #if CLIENT speaker.SetCustomInteract( TryStartConversation, diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs index f2c00b386..9fc395d0a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs @@ -18,14 +18,13 @@ namespace Barotrauma public MissionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { - //TODO: use event identifier in the error messages if (string.IsNullOrEmpty(MissionIdentifier) && string.IsNullOrEmpty(MissionTag)) { - DebugConsole.ThrowError($"Error in event \"{"event identifier goes here"}\": neither MissionIdentifier or MissionTag has been configured."); + DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured."); } if (!string.IsNullOrEmpty(MissionIdentifier) && !string.IsNullOrEmpty(MissionTag)) { - DebugConsole.ThrowError($"Error in event \"{"event identifier goes here"}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored."); + DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored."); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs index fa8733a5b..1efea1e0c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/SpawnAction.cs @@ -240,7 +240,7 @@ namespace Barotrauma return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.ToEnumerable()); } - public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable moduleFlags = null, IEnumerable spawnpointTags = null) + public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable moduleFlags = null, IEnumerable spawnpointTags = null, bool asFarAsPossibleFromAirlock = false) { List potentialSpawnPoints = spawnLocation switch { @@ -253,6 +253,7 @@ namespace Barotrauma }; potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && !wp.isObstructed); + var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false).ToList(); if (moduleFlags != null && moduleFlags.Any()) { @@ -282,7 +283,7 @@ namespace Barotrauma IEnumerable validSpawnPoints; if (spawnPointType.HasValue) { - validSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.SpawnType == spawnPointType.Value); + validSpawnPoints = potentialSpawnPoints.FindAll(wp => spawnPointType.Value.HasFlag(wp.SpawnType)); } else { @@ -291,7 +292,6 @@ namespace Barotrauma } //don't spawn in an airlock module if there are other options - var airlockSpawnPoints = validSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false); if (airlockSpawnPoints.Count() < validSpawnPoints.Count()) { validSpawnPoints = validSpawnPoints.Except(airlockSpawnPoints); @@ -313,7 +313,25 @@ namespace Barotrauma } } - return validSpawnPoints.GetRandom(); + if (asFarAsPossibleFromAirlock && airlockSpawnPoints.Any()) + { + WayPoint furthestPoint = validSpawnPoints.First(); + float furthestDist = 0.0f; + foreach (WayPoint waypoint in validSpawnPoints) + { + float dist = Vector2.DistanceSquared(waypoint.WorldPosition, airlockSpawnPoints.First().WorldPosition); + if (dist > furthestDist) + { + furthestDist = dist; + furthestPoint = waypoint; + } + } + return furthestPoint; + } + else + { + return validSpawnPoints.GetRandom(); + } } public override string ToDebugString() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs index 6c9d6f5aa..46ddfffbc 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; using Barotrauma.Extensions; +using NLog; namespace Barotrauma { @@ -13,7 +14,8 @@ namespace Barotrauma { CONVERSATION, STATUSEFFECT, - MISSION + MISSION, + UNLOCKPATH } const float IntensityUpdateInterval = 5.0f; @@ -111,21 +113,50 @@ namespace Barotrauma SelectSettings(); - var initialEventSet = SelectRandomEvents(EventSet.List); - if (initialEventSet != null) + int seed = 0; + if (level != null) { - pendingEventSets.Add(initialEventSet); - int seed = ToolBox.StringToInt(level.Seed); + seed = ToolBox.StringToInt(level.Seed); foreach (var previousEvent in level.LevelData.EventHistory) { seed ^= ToolBox.StringToInt(previousEvent.Identifier); } - MTRandom rand = new MTRandom(seed); + } + MTRandom rand = new MTRandom(seed); + + var initialEventSet = SelectRandomEvents(EventSet.List); + if (initialEventSet != null) + { + pendingEventSets.Add(initialEventSet); CreateEvents(initialEventSet, rand); } if (level?.LevelData?.Type == LevelData.LevelType.Outpost) { + //if the outpost is connected to a locked connection, create an event to unlock it + if (level.StartLocation?.Connections.Any(c => c.Locked) ?? false) + { + var unlockPathPrefabs = EventSet.PrefabList.FindAll(e => e.UnlockPathEvent); + var unlockPathPrefabsForBiome = unlockPathPrefabs.FindAll(e => + string.IsNullOrEmpty(e.BiomeIdentifier) || + e.BiomeIdentifier.Equals(level.LevelData.Biome.Identifier, StringComparison.OrdinalIgnoreCase)); + + var unlockPathEventPrefab = unlockPathPrefabsForBiome.Any() ? + ToolBox.SelectWeightedRandom(unlockPathPrefabsForBiome, unlockPathPrefabsForBiome.Select(b => b.Commonness).ToList(), rand) : + ToolBox.SelectWeightedRandom(unlockPathPrefabs, unlockPathPrefabs.Select(b => b.Commonness).ToList(), rand); + if (unlockPathEventPrefab != null) + { + var newEvent = unlockPathEventPrefab.CreateInstance(); + newEvent.Init(true); + ActiveEvents.Add(newEvent); + } + else + { + //if no event that unlocks the path can be found, unlock it automatically + level.StartLocation.Connections.ForEach(c => c.Locked = false); + } + } + level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e))); if (level.LevelData.EventHistory.Count > MaxEventHistory) { @@ -362,20 +393,24 @@ namespace Barotrauma } else if (eventSet.PerWreck) { - var wrecks = Submarine.Loaded.Where(s => s.Info.IsWreck && (s.WreckAI == null || !s.WreckAI.IsAlive)); + var wrecks = Submarine.Loaded.Where(s => s.Info.IsWreck && (s.WreckAI == null || !s.WreckAI.IsAlive)); applyCount = wrecks.Count(); foreach (var wreck in wrecks) { spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Submarine == wreck; }); } } + + var suitablePrefabs = eventSet.EventPrefabs.FindAll(e => + string.IsNullOrEmpty(e.First.BiomeIdentifier) || + e.First.BiomeIdentifier.Equals(Level.Loaded.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase)); for (int i = 0; i < applyCount; i++) { if (eventSet.ChooseRandom) { - if (eventSet.EventPrefabs.Count > 0) + if (suitablePrefabs.Count > 0) { - List> unusedEvents = new List>(eventSet.EventPrefabs); + List> unusedEvents = new List>(suitablePrefabs); for (int j = 0; j < eventSet.EventCount; j++) { var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e)).ToList(), rand); @@ -403,7 +438,7 @@ namespace Barotrauma } else { - foreach (Pair eventPrefab in eventSet.EventPrefabs) + foreach (Pair eventPrefab in suitablePrefabs) { var newEvent = eventPrefab.First.CreateInstance(); if (newEvent == null) { continue; } @@ -430,12 +465,19 @@ namespace Barotrauma MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed)); var allowedEventSets = - eventSets.Where(es => level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty && level.LevelData.Type == es.LevelType); + eventSets.Where(es => + level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty && + level.LevelData.Type == es.LevelType && + (string.IsNullOrEmpty(es.BiomeIdentifier) || es.BiomeIdentifier.Equals(level.LevelData.Biome.Identifier, StringComparison.OrdinalIgnoreCase))); - LocationType locationType = (GameMain.GameSession?.GameMode as CampaignMode)?.Map?.CurrentLocation?.Type ?? level?.StartLocation?.Type; - if (locationType != null) + Location location = (GameMain.GameSession?.GameMode as CampaignMode)?.Map?.CurrentLocation ?? level?.StartLocation; + LocationType locationType = location?.GetLocationType(); + + if (location != null) { - allowedEventSets = allowedEventSets.Where(set => set.LocationTypeIdentifiers == null || set.LocationTypeIdentifiers.Any(identifier => string.Equals(identifier, locationType.Identifier, StringComparison.OrdinalIgnoreCase))); + allowedEventSets = allowedEventSets.Where(set => + set.LocationTypeIdentifiers == null || + set.LocationTypeIdentifiers.Any(identifier => string.Equals(identifier, locationType.Identifier, StringComparison.OrdinalIgnoreCase))); } float totalCommonness = allowedEventSets.Sum(e => e.GetCommonness(level)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventPrefab.cs index b2fc0cb84..aa02f7a66 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventPrefab.cs @@ -12,6 +12,8 @@ namespace Barotrauma public readonly bool TriggerEventCooldown; public float Commonness; public string Identifier; + public bool UnlockPathEvent; + public string BiomeIdentifier; public EventPrefab(XElement element) { @@ -34,6 +36,8 @@ namespace Barotrauma Commonness = element.GetAttributeFloat("commonness", 1.0f); SpawnProbability = Math.Clamp(element.GetAttributeFloat("spawnprobability", 1.0f), 0, 1); TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true); + UnlockPathEvent = element.GetAttributeBool("unlockpathevent", false); + BiomeIdentifier = ConfigElement.GetAttributeString("biome", string.Empty); } public Event CreateInstance() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs index 050b131e5..476c1e173 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs @@ -65,6 +65,8 @@ namespace Barotrauma //0-100 public readonly float MinLevelDifficulty, MaxLevelDifficulty; + public readonly string BiomeIdentifier; + public readonly LevelData.LevelType LevelType; public readonly string[] LocationTypeIdentifiers; @@ -111,6 +113,7 @@ namespace Barotrauma EventPrefabs = new List>(); ChildSets = new List(); + BiomeIdentifier = element.GetAttributeString("biome", string.Empty); MinLevelDifficulty = element.GetAttributeFloat("minleveldifficulty", 0); MaxLevelDifficulty = Math.Max(element.GetAttributeFloat("maxleveldifficulty", 100), MinLevelDifficulty); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs index 93c2caa2f..35e96f23e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/AbandonedOutpostMission.cs @@ -1,5 +1,7 @@ using Barotrauma.Extensions; +using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Linq; namespace Barotrauma @@ -8,38 +10,33 @@ namespace Barotrauma { private readonly XElement characterConfig; - private readonly List characters = new List(); + protected readonly List characters = new List(); private readonly Dictionary> characterItems = new Dictionary>(); + protected readonly HashSet requireKill = new HashSet(); + protected readonly HashSet requireRescue = new HashSet(); - private readonly string itemTag; + public override bool AllowRespawn => false; - private Item itemToDestroy; + protected bool wasDocked; public AbandonedOutpostMission(MissionPrefab prefab, Location[] locations) : base(prefab, locations) { characterConfig = prefab.ConfigElement.Element("Characters"); - - itemTag = prefab.ConfigElement.GetAttributeString("targetitem", ""); - if (string.IsNullOrEmpty(itemTag)) - { - DebugConsole.ThrowError($"Error in mission prefab \"{prefab.Identifier}\". Target item not defined."); - } } protected override void StartMissionSpecific(Level level) { - itemToDestroy = null; - itemToDestroy = Item.ItemList.Find(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag)); - if (itemToDestroy == null) - { - DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\"."); - } - + characters.Clear(); + characterItems.Clear(); + requireKill.Clear(); + requireRescue.Clear(); if (!IsClient) { InitCharacters(); } + + wasDocked = Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost); } private void InitCharacters() @@ -57,48 +54,139 @@ namespace Barotrauma foreach (XElement element in characterConfig.Elements()) { - string characterIdentifier = element.GetAttributeString("identifier", ""); - string characterFrom = element.GetAttributeString("from", ""); - HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier); - if (humanPrefab == null) - { - DebugConsole.ThrowError("Couldn't spawn character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found"); - return; - } + if (GameMain.NetworkMember == null && element.GetAttributeBool("multiplayeronly", false)) { continue; } - string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null); - string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null); - ISpatialEntity spawnPos = SpawnAction.GetSpawnPos( - SpawnAction.SpawnLocationType.Outpost, SpawnType.Human, - moduleFlags ?? humanPrefab.GetModuleFlags(), - spawnPointTags ?? humanPrefab.GetSpawnPointTags()); - if (spawnPos == null) + int defaultCount = element.GetAttributeInt("count", -1); + if (defaultCount < 0) { - spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom(); + defaultCount = element.GetAttributeInt("amount", 1); } + int min = Math.Min(element.GetAttributeInt("min", defaultCount), 255); + int max = Math.Min(Math.Max(min, element.GetAttributeInt("max", defaultCount)), 255); + int count = Rand.Range(min, max + 1); - var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server); - Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, spawnPos.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false); + if (element.Attribute("identifier") != null && element.Attribute("from") != null) + { + string characterIdentifier = element.GetAttributeString("identifier", ""); + string characterFrom = element.GetAttributeString("from", ""); + HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier); + if (humanPrefab == null) + { + DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found"); + continue; + } + for (int i = 0; i < count; i++) + { + LoadHuman(humanPrefab, element, submarine); + } + } + else + { + string speciesName = element.GetAttributeString("character", element.GetAttributeString("identifier", "")); + var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); + if (characterPrefab == null) + { + DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + speciesName + "\" not found"); + continue; + } + for (int i = 0; i < count; i++) + { + LoadMonster(characterPrefab, element, submarine); + } + } + } + } + + private void LoadHuman(HumanPrefab humanPrefab, XElement element, Submarine submarine) + { + string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null); + string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null); + ISpatialEntity spawnPos = SpawnAction.GetSpawnPos( + SpawnAction.SpawnLocationType.Outpost, SpawnType.Human, + moduleFlags ?? humanPrefab.GetModuleFlags(), + spawnPointTags ?? humanPrefab.GetSpawnPointTags(), + element.GetAttributeBool("asfaraspossible", false)); + if (spawnPos == null) + { + spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom(); + } + + var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server); + Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, spawnPos.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false); + if (element.GetAttributeBool("requirerescue", false)) + { + requireRescue.Add(spawnedCharacter); + spawnedCharacter.TeamID = CharacterTeamType.FriendlyNPC; +#if CLIENT + GameMain.GameSession.CrewManager.AddCharacterToCrewList(spawnedCharacter); +#endif + } + else + { spawnedCharacter.TeamID = CharacterTeamType.None; - humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos); - humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false); + } + humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos); + humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false); + if (element.GetAttributeBool("requirekill", false)) + { + requireKill.Add(spawnedCharacter); + } + characters.Add(spawnedCharacter); + characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true)); + } - characters.Add(spawnedCharacter); + private void LoadMonster(CharacterPrefab monsterPrefab, XElement element, Submarine submarine) + { + string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null); + string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null); + ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Enemy, moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false)); + if (spawnPos == null) + { + spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom(); + } + Character spawnedCharacter = Character.Create(monsterPrefab.Identifier, spawnPos.WorldPosition, ToolBox.RandomSeed(8), createNetworkEvent: false); + characters.Add(spawnedCharacter); + if (element.GetAttributeBool("requirekill", false)) + { + requireKill.Add(spawnedCharacter); + } + if (spawnedCharacter.Inventory != null) + { characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true)); } } + public override void Update(float deltaTime) { - if (State == 0 && itemToDestroy != null && itemToDestroy.Condition <= 0.0f) + switch (state) { - State = 1; + case 0: + if (requireKill.All(c => c.Removed || c.IsDead) && + requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player)) + { + State = 1; + } + break; +#if SERVER + case 1: + if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null) + { + if (!Submarine.MainSub.AtStartPosition || (wasDocked && !Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost))) + { + GameMain.Server.EndGame(); + State = 2; + } + } + break; +#endif } + } public override void End() { - completed = itemToDestroy == null || itemToDestroy.Condition <= 0.0f; + completed = State > 0; if (completed) { if (Prefab.LocationTypeChangeOnCompleted != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs index c43efd9f1..1c8f50831 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs @@ -43,21 +43,21 @@ namespace Barotrauma public virtual string SuccessMessage { get { return successMessage; } - private set { successMessage = value; } + //private set { successMessage = value; } } private string failureMessage; public virtual string FailureMessage { get { return failureMessage; } - private set { failureMessage = value; } + //private set { failureMessage = value; } } protected string description; public virtual string Description { get { return description; } - private set { description = value; } + //private set { description = value; } } public int Reward @@ -110,7 +110,7 @@ namespace Barotrauma description = prefab.Description; successMessage = prefab.SuccessMessage; - FailureMessage = prefab.FailureMessage; + failureMessage = prefab.FailureMessage; Headers = new List(prefab.Headers); Messages = new List(prefab.Messages); @@ -118,12 +118,13 @@ namespace Barotrauma for (int n = 0; n < 2; n++) { - if (description != null) description = description.Replace("[location" + (n + 1) + "]", locations[n].Name); - if (successMessage != null) successMessage = successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); - if (failureMessage != null) failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); + string locationName = $"‖color:gui.orange‖{locations[n].Name}‖end‖"; + if (description != null) description = description.Replace("[location" + (n + 1) + "]", locationName); + if (successMessage != null) successMessage = successMessage.Replace("[location" + (n + 1) + "]", locationName); + if (failureMessage != null) failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locationName); for (int m = 0; m < Messages.Count; m++) { - Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name); + Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locationName); } } if (description != null) description = description.Replace("[reward]", Reward.ToString("N0")); @@ -180,7 +181,7 @@ namespace Barotrauma { foreach (string categoryToShow in Prefab.UnhideEntitySubCategories) { - foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab.HasSubCategory(categoryToShow))) + foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab?.HasSubCategory(categoryToShow) ?? false)) { entityToShow.HiddenInGame = false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs index 49b4ae424..9d48ab4c3 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MissionPrefab.cs @@ -18,9 +18,10 @@ namespace Barotrauma Nest = 0x10, Mineral = 0x20, Combat = 0x40, - AbandonedOutpost = 0x80, + OutpostDestroy = 0x80, + OutpostRescue = 0x100, - All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | AbandonedOutpost + All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat | OutpostDestroy | OutpostRescue } partial class MissionPrefab @@ -35,7 +36,8 @@ namespace Barotrauma { MissionType.Beacon, typeof(BeaconMission) }, { MissionType.Nest, typeof(NestMission) }, { MissionType.Mineral, typeof(MineralMission) }, - { MissionType.AbandonedOutpost, typeof(AbandonedOutpostMission) }, + { MissionType.OutpostDestroy, typeof(OutpostDestroyMission) }, + { MissionType.OutpostRescue, typeof(AbandonedOutpostMission) }, }; public static readonly Dictionary PvPMissionClasses = new Dictionary() { @@ -166,7 +168,10 @@ namespace Barotrauma FailureMessage = element.GetAttributeString("failuremessage", ""); } - SonarLabel = TextManager.Get("MissionSonarLabel." + TextIdentifier, true) ?? element.GetAttributeString("sonarlabel", ""); + SonarLabel = + TextManager.Get("MissionSonarLabel." + TextIdentifier, true) ?? + TextManager.Get("MissionSonarLabel." + element.GetAttributeString("sonarlabel", ""), true) ?? + element.GetAttributeString("sonarlabel", ""); SonarIconIdentifier = element.GetAttributeString("sonaricon", ""); MultiplayerOnly = element.GetAttributeBool("multiplayeronly", false); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs index a2ff73335..29641f40d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/MonsterMission.cs @@ -8,7 +8,7 @@ namespace Barotrauma partial class MonsterMission : Mission { //string = filename, point = min,max - private readonly HashSet> monsterPrefabs = new HashSet>(); + private readonly HashSet<(CharacterPrefab character, Point amountRange)> monsterPrefabs = new HashSet<(CharacterPrefab character, Point amountRange)>(); private readonly List monsters = new List(); private readonly List sonarPositions = new List(); @@ -43,7 +43,7 @@ namespace Barotrauma if (characterPrefab != null) { int monsterCount = Math.Min(prefab.ConfigElement.GetAttributeInt("monstercount", 1), 255); - monsterPrefabs.Add(new Tuple(characterPrefab, new Point(monsterCount))); + monsterPrefabs.Add((characterPrefab, new Point(monsterCount))); } else { @@ -73,7 +73,7 @@ namespace Barotrauma var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName); if (characterPrefab != null) { - monsterPrefabs.Add(new Tuple(characterPrefab, new Point(min, max))); + monsterPrefabs.Add((characterPrefab, new Point(min, max))); } else { @@ -83,7 +83,7 @@ namespace Barotrauma if (monsterPrefabs.Any()) { - var characterParams = new CharacterParams(monsterPrefabs.First().Item1.FilePath); + var characterParams = new CharacterParams(monsterPrefabs.First().character.FilePath); description = description.Replace("[monster]", TextManager.Get("character." + characterParams.SpeciesTranslationOverride, returnNull: true) ?? TextManager.Get("character." + characterParams.SpeciesName)); @@ -115,12 +115,12 @@ namespace Barotrauma if (!IsClient) { Level.Loaded.TryGetInterestingPosition(true, spawnPosType, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos); - foreach (var monster in monsterPrefabs) + foreach (var (character, amountRange) in monsterPrefabs) { - int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1); + int amount = Rand.Range(amountRange.X, amountRange.Y + 1); for (int i = 0; i < amount; i++) { - monsters.Add(Character.Create(monster.Item1.Identifier, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false)); + monsters.Add(Character.Create(character.Identifier, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false)); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/MonsterEvent.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/MonsterEvent.cs index 9e33ac3c1..80ac6f8ac 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/MonsterEvent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/MonsterEvent.cs @@ -18,12 +18,14 @@ namespace Barotrauma private Vector2? spawnPos; - private readonly bool disallowed; + private bool disallowed; private readonly Level.PositionType spawnPosType; private bool spawnPending; + private int maxAmountPerLevel = int.MaxValue; + public List Monsters => monsters; public Vector2? SpawnPos => spawnPos; public bool SpawnPending => spawnPending; @@ -70,6 +72,8 @@ namespace Barotrauma minAmount = prefab.ConfigElement.GetAttributeInt("minamount", defaultAmount); maxAmount = Math.Max(prefab.ConfigElement.GetAttributeInt("maxamount", 1), minAmount); + maxAmountPerLevel = prefab.ConfigElement.GetAttributeInt("maxamountperlevel", int.MaxValue); + var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", ""); if (string.IsNullOrWhiteSpace(spawnPosTypeStr) || !Enum.TryParse(spawnPosTypeStr, true, out spawnPosType)) @@ -345,6 +349,15 @@ namespace Barotrauma if (spawnPos == null) { + if (maxAmountPerLevel < int.MaxValue) + { + if (Character.CharacterList.Count(c => c.SpeciesName == speciesName) >= maxAmountPerLevel) + { + disallowed = true; + return; + } + } + FindSpawnPosition(affectSubImmediately: true); //the event gets marked as finished if a spawn point is not found if (isFinished) { return; } @@ -400,7 +413,7 @@ namespace Barotrauma foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineType.Player) { continue; } - if (submarine.WorldPosition.Y > Level.Loaded.AbyssStart) + if (submarine.WorldPosition.Y > 0) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CrewManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CrewManager.cs index 84b86b873..0c471748b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/CrewManager.cs @@ -242,6 +242,21 @@ namespace Barotrauma conversationTimer = IsSinglePlayer ? Rand.Range(5.0f, 10.0f) : Rand.Range(45.0f, 60.0f); } + public void RenameCharacter(CharacterInfo characterInfo, string newName) + { + int identifier = characterInfo.GetIdentifierUsingOriginalName(); + var match = characterInfos.FirstOrDefault(ci => ci.GetIdentifierUsingOriginalName() == identifier); + if (match == null) + { + DebugConsole.ThrowError($"Tried to rename an invalid crew member ({identifier})"); + return; + } + match.Rename(newName); + RenameCharacterProjSpecific(match); + } + + partial void RenameCharacterProjSpecific(CharacterInfo characterInfo); + public void FireCharacter(CharacterInfo characterInfo) { RemoveCharacterInfo(characterInfo); @@ -277,6 +292,7 @@ namespace Barotrauma private void UpdateConversations(float deltaTime) { + if (GameMain.GameSession?.GameMode?.Preset == GameModePreset.TestMode) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.ServerSettings.DisableBotConversations) { return; } conversationTimer -= deltaTime; @@ -305,19 +321,33 @@ namespace Barotrauma { List availableSpeakers = new List() { npc, player }; List dialogFlags = new List() { "OutpostNPC", "EnterOutpost" }; - if (GameMain.GameSession?.GameMode is CampaignMode campaignMode && campaignMode.Map?.CurrentLocation?.Reputation != null) + if (GameMain.GameSession?.GameMode is CampaignMode campaignMode) { - float normalizedReputation = MathUtils.InverseLerp( - campaignMode.Map.CurrentLocation.Reputation.MinReputation, - campaignMode.Map.CurrentLocation.Reputation.MaxReputation, - campaignMode.Map.CurrentLocation.Reputation.Value); - if (normalizedReputation < 0.2f) + if (campaignMode.Map?.CurrentLocation?.Type?.Identifier.Equals("abandoned", StringComparison.OrdinalIgnoreCase) ?? false) { - dialogFlags.Add("LowReputation"); + if (npc.TeamID == CharacterTeamType.None) + { + dialogFlags.Add("Bandit"); + } + else if (npc.TeamID == CharacterTeamType.FriendlyNPC) + { + dialogFlags.Add("Hostage"); + } } - else if (normalizedReputation > 0.8f) + else if (campaignMode.Map?.CurrentLocation?.Reputation != null) { - dialogFlags.Add("HighReputation"); + float normalizedReputation = MathUtils.InverseLerp( + campaignMode.Map.CurrentLocation.Reputation.MinReputation, + campaignMode.Map.CurrentLocation.Reputation.MaxReputation, + campaignMode.Map.CurrentLocation.Reputation.Value); + if (normalizedReputation < 0.2f) + { + dialogFlags.Add("LowReputation"); + } + else if (normalizedReputation > 0.8f) + { + dialogFlags.Add("HighReputation"); + } } } pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers, dialogFlags)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs index af6d43366..8cf919a8f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs @@ -5,9 +5,39 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; +using Barotrauma.Networking; namespace Barotrauma { + internal struct CampaignSettings + { + public static CampaignSettings Empty = new CampaignSettings(); + + // Anything that uses this field I wasn't sure if actually needed the proper campaign settings to be passed down + public static CampaignSettings Unsure = Empty; + public bool RadiationEnabled { get; set; } + + public CampaignSettings(IReadMessage inc) + { + RadiationEnabled = inc.ReadBoolean(); + } + + public CampaignSettings(XElement element) + { + RadiationEnabled = element.GetAttributeBool(nameof(RadiationEnabled).ToLower(), true); + } + + public void Serialize(IWriteMessage msg) + { + msg.Write(RadiationEnabled); + } + + public XElement Save() + { + return new XElement(nameof(CampaignSettings), new XAttribute(nameof(RadiationEnabled).ToLower(), RadiationEnabled)); + } + } + abstract partial class CampaignMode : GameMode { const int MaxMoney = int.MaxValue / 2; //about 1 billion @@ -31,6 +61,8 @@ namespace Barotrauma protected XElement petsElement; + public CampaignSettings Settings; + private List extraMissions = new List(); public enum TransitionType @@ -224,7 +256,7 @@ namespace Barotrauma if (levelData.HasBeaconStation && !levelData.IsBeaconActive) { - var beaconMissionPrefab = MissionPrefab.List.Find(m => m.Identifier.Equals("beaconnoreward", StringComparison.OrdinalIgnoreCase)); + var beaconMissionPrefab = MissionPrefab.List.Find(m => m.Tags.Any(t => t.Equals("beaconnoreward", StringComparison.OrdinalIgnoreCase))); if (beaconMissionPrefab != null && !Missions.Any(m => m.Prefab.Type == beaconMissionPrefab.Type)) { extraMissions.Add(beaconMissionPrefab.Instantiate(Map.SelectedConnection.Locations)); @@ -232,8 +264,12 @@ namespace Barotrauma } if (levelData.HasHuntingGrounds) { - var huntingGroundsMissionPrefab = MissionPrefab.List.Find(m => m.Identifier.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase)); - if (huntingGroundsMissionPrefab != null && !Missions.Any(m => m.Prefab.Type == huntingGroundsMissionPrefab.Type)) + var huntingGroundsMissionPrefab = MissionPrefab.List.Find(m => m.Tags.Any(t => t.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase))); + if (huntingGroundsMissionPrefab == null) + { + DebugConsole.AddWarning("Could not find a hunting grounds mission for the level. No mission with the tag \"huntinggroundsnoreward\" found."); + } + else if (!Missions.Any(m => m.Prefab.Type == huntingGroundsMissionPrefab.Type)) { extraMissions.Add(huntingGroundsMissionPrefab.Instantiate(Map.SelectedConnection.Locations)); } @@ -342,7 +378,7 @@ namespace Barotrauma nextLevel = map.StartLocation.LevelData; return TransitionType.End; } - if (Level.Loaded.EndLocation != null && Level.Loaded.EndLocation.HasOutpost() && Level.Loaded.EndOutpost != null) + if (Level.Loaded.EndLocation != null && Level.Loaded.EndLocation.Type.HasOutpost && Level.Loaded.EndOutpost != null) { nextLevel = Level.Loaded.EndLocation.LevelData; return TransitionType.ProgressToNextLocation; @@ -361,12 +397,12 @@ namespace Barotrauma } else if (leavingSub.AtStartPosition) { - if (map.CurrentLocation.HasOutpost() && Level.Loaded.StartOutpost != null) + if (map.CurrentLocation.Type.HasOutpost && Level.Loaded.StartOutpost != null) { nextLevel = map.CurrentLocation.LevelData; return TransitionType.ReturnToPreviousLocation; } - else if (map.SelectedLocation != null && map.SelectedLocation != map.CurrentLocation && !map.CurrentLocation.HasOutpost() && + else if (map.SelectedLocation != null && map.SelectedLocation != map.CurrentLocation && !map.CurrentLocation.Type.HasOutpost && map.SelectedConnection != null && Level.Loaded.LevelData != map.SelectedConnection.LevelData) { nextLevel = map.SelectedConnection.LevelData; @@ -584,14 +620,12 @@ namespace Barotrauma public bool TryHireCharacter(Location location, CharacterInfo characterInfo) { + if (characterInfo == null) { return false; } if (Money < characterInfo.Salary) { return false; } - characterInfo.IsNewHire = true; - location.RemoveHireableCharacter(characterInfo); CrewManager.AddCharacterInfo(characterInfo); Money -= characterInfo.Salary; - return true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs index 608668d29..6bbfa5cfb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -59,13 +59,14 @@ namespace Barotrauma InitCampaignData(); } - public static MultiPlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub) + public static MultiPlayerCampaign StartNew(string mapSeed, SubmarineInfo selectedSub, CampaignSettings settings) { MultiPlayerCampaign campaign = new MultiPlayerCampaign(); //only the server generates the map, the clients load it from a save file if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { - campaign.map = new Map(campaign, mapSeed); + campaign.map = new Map(campaign, mapSeed, settings); + campaign.Settings = settings; } campaign.InitProjSpecific(); return campaign; @@ -128,11 +129,14 @@ namespace Barotrauma { switch (subElement.Name.ToString().ToLowerInvariant()) { + case "campaignsettings": + Settings = new CampaignSettings(subElement); + break; case "map": if (map == null) { //map not created yet, loading this campaign for the first time - map = Map.Load(this, subElement); + map = Map.Load(this, subElement, Settings); } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs index a1a78de87..14ef85c3c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs @@ -103,12 +103,12 @@ namespace Barotrauma /// /// Start a new GameSession. Will be saved to the specified save path (if playing a game mode that can be saved). /// - public GameSession(SubmarineInfo submarineInfo, string savePath, GameModePreset gameModePreset, string seed = null, MissionType missionType = MissionType.None) + public GameSession(SubmarineInfo submarineInfo, string savePath, GameModePreset gameModePreset, CampaignSettings settings, string seed = null, MissionType missionType = MissionType.None) : this(submarineInfo) { this.SavePath = savePath; CrewManager = new CrewManager(gameModePreset != null && gameModePreset.IsSinglePlayer); - GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, missionType: missionType); + GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, settings, missionType: missionType); } /// @@ -118,14 +118,13 @@ namespace Barotrauma : this(submarineInfo) { CrewManager = new CrewManager(gameModePreset != null && gameModePreset.IsSinglePlayer); - GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, missionPrefabs: missionPrefabs); + GameMode = InstantiateGameMode(gameModePreset, seed, submarineInfo, CampaignSettings.Empty, missionPrefabs: missionPrefabs); } /// /// Load a game session from the specified XML document. The session will be saved to the specified path. /// - public GameSession(SubmarineInfo submarineInfo, List ownedSubmarines, XDocument doc, string saveFile) - : this(submarineInfo, ownedSubmarines) + public GameSession(SubmarineInfo submarineInfo, List ownedSubmarines, XDocument doc, string saveFile) : this(submarineInfo, ownedSubmarines) { this.SavePath = saveFile; GameMain.GameSession = this; @@ -159,7 +158,7 @@ namespace Barotrauma } } - private GameMode InstantiateGameMode(GameModePreset gameModePreset, string seed, SubmarineInfo selectedSub, IEnumerable missionPrefabs = null, MissionType missionType = MissionType.None) + private GameMode InstantiateGameMode(GameModePreset gameModePreset, string seed, SubmarineInfo selectedSub, CampaignSettings settings, IEnumerable missionPrefabs = null, MissionType missionType = MissionType.None) { if (gameModePreset.GameModeType == typeof(CoOpMode)) { @@ -175,7 +174,7 @@ namespace Barotrauma } else if (gameModePreset.GameModeType == typeof(MultiPlayerCampaign)) { - var campaign = MultiPlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub); + var campaign = MultiPlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub, settings); if (campaign != null && selectedSub != null) { campaign.Money = Math.Max(MultiPlayerCampaign.MinimumInitialMoney, campaign.Money - selectedSub.Price); @@ -185,7 +184,7 @@ namespace Barotrauma #if CLIENT else if (gameModePreset.GameModeType == typeof(SinglePlayerCampaign)) { - var campaign = SinglePlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub); + var campaign = SinglePlayerCampaign.StartNew(seed ?? ToolBox.RandomSeed(8), selectedSub, settings); if (campaign != null && selectedSub != null) { campaign.Money = Math.Max(SinglePlayerCampaign.MinimumInitialMoney, campaign.Money - selectedSub.Price); @@ -400,6 +399,8 @@ namespace Barotrauma } GUI.PreventPauseMenuToggle = false; + + HintManager.OnRoundStarted(); #endif } @@ -467,7 +468,7 @@ namespace Barotrauma { mpCampaign.CargoManager.CreatePurchasedItems(); #if SERVER - mpCampaign.SendCrewState(false, null); + mpCampaign.SendCrewState(null, default, null); #endif } mpCampaign.UpgradeManager.ApplyUpgrades(); @@ -544,7 +545,7 @@ namespace Barotrauma { Submarine.SetPosition(spawnPos); myPort.Dock(outPostPort); - myPort.Lock(true, forcePosition: true, applyEffects: false); + myPort.Lock(isNetworkMessage: true, applyEffects: false); } else { @@ -583,9 +584,10 @@ namespace Barotrauma { EventManager?.Update(deltaTime); GameMode?.Update(deltaTime); - foreach (Mission mission in missions) + //backwards for loop because the missions may get completed and removed from the list in Update() + for (int i = missions.Count - 1; i >= 0; i--) { - mission.Update(deltaTime); + missions[i].Update(deltaTime); } UpdateProjSpecific(deltaTime); } @@ -636,6 +638,10 @@ namespace Barotrauma StatusEffect.StopAll(); missions.Clear(); IsRunning = false; + +#if CLIENT + HintManager.OnRoundEnded(); +#endif } public void KillCharacter(Character character) diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSession/HireManager.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSession/HireManager.cs index 1d6f6529d..59b6e4bc5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSession/HireManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSession/HireManager.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; namespace Barotrauma { @@ -39,5 +39,12 @@ namespace Barotrauma AvailableCharacters.ForEach(c => c.Remove()); AvailableCharacters.Clear(); } + + public void RenameCharacter(CharacterInfo characterInfo, string newName) + { + if (characterInfo == null || string.IsNullOrEmpty(newName)) { return; } + AvailableCharacters.FirstOrDefault(ci => ci == characterInfo)?.Rename(newName); + PendingHires.FirstOrDefault(ci => ci == characterInfo)?.Rename(newName); + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/GameSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/GameSettings.cs index 99ce67c26..4d69e9155 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/GameSettings.cs @@ -171,7 +171,7 @@ namespace Barotrauma /// /// How many corpses there can be in a sub before they start to get despawned /// - public int CorpsesPerSubDespawnThreshold { get; set; } = 5; + public int CorpsesPerSubDespawnThreshold { get; set; } = 10; private string overrideSaveFolder, overrideMultiplayerSaveFolder; @@ -301,10 +301,14 @@ namespace Barotrauma public volatile bool WaitingForAutoUpdate; + public bool DisableInGameHints { get; set; } + #if DEBUG public bool AutomaticQuickStartEnabled { get; set; } public bool AutomaticCampaignLoadEnabled { get; set; } public bool TextManagerDebugModeEnabled { get; set; } + + public bool ModBreakerMode { get; set; } #endif private System.IO.FileSystemWatcher modsFolderWatcher; @@ -735,6 +739,10 @@ namespace Barotrauma private bool textScaleDirty; public List CompletedTutorialNames { get; private set; } + /// + /// Identifiers of hints the player has chosen not to see again + /// + public HashSet IgnoredHints { get; private set; } = new HashSet(); public HashSet EncounteredCreatures { get; private set; } = new HashSet(); public HashSet KilledCreatures { get; private set; } = new HashSet(); @@ -1151,6 +1159,12 @@ namespace Barotrauma CompletedTutorialNames.Add(element.GetAttributeString("name", "")); } } + + if (doc.Root.Element("ignoredhints") is XElement ignoredHintsElement) + { + IgnoredHints = new HashSet(ignoredHintsElement.GetAttributeStringArray("identifiers", new string[0], convertToLowerInvariant: true)); + } + XElement encounters = doc.Root.Element("encountered"); if (encounters != null) { @@ -1172,7 +1186,7 @@ namespace Barotrauma #endregion #region Save PlayerConfig - public void SaveNewPlayerConfig() + public bool SaveNewPlayerConfig() { XDocument doc = new XDocument(); UnsavedSettings = false; @@ -1211,11 +1225,13 @@ namespace Barotrauma new XAttribute("tutorialskipwarning", ShowTutorialSkipWarning), new XAttribute("corpsedespawndelay", CorpseDespawnDelay), new XAttribute("corpsespersubdespawnthreshold", CorpsesPerSubDespawnThreshold), - new XAttribute("usedualmodesockets", UseDualModeSockets) + new XAttribute("usedualmodesockets", UseDualModeSockets), + new XAttribute("disableingamehints", DisableInGameHints) #if DEBUG , new XAttribute("automaticquickstartenabled", AutomaticQuickStartEnabled) , new XAttribute("automaticcampaignloadenabled", AutomaticCampaignLoadEnabled) , new XAttribute("textmanagerdebugmodeenabled", TextManagerDebugModeEnabled) + , new XAttribute("modbreakermode", ModBreakerMode) #endif ); @@ -1403,6 +1419,8 @@ namespace Barotrauma } doc.Root.Add(tutorialElement); + doc.Root.Add(new XElement("ignoredhints", new XAttribute("identifiers", string.Join(",", IgnoredHints).Trim().ToLowerInvariant()))); + doc.Root.Add(new XElement("encountered", new XAttribute("creatures", string.Join(",", EncounteredCreatures).Trim().ToLowerInvariant()))); doc.Root.Add(new XElement("killed", new XAttribute("creatures", string.Join(",", KilledCreatures).Trim().ToLowerInvariant()))); @@ -1426,7 +1444,10 @@ namespace Barotrauma DebugConsole.ThrowError("Saving game settings failed.", e); GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, "Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace()); + return false; } + + return true; } #endregion @@ -1460,10 +1481,12 @@ namespace Barotrauma EditorDisclaimerShown = doc.Root.GetAttributeBool("editordisclaimershown", EditorDisclaimerShown); ShowTutorialSkipWarning = doc.Root.GetAttributeBool("tutorialskipwarning", true); UseDualModeSockets = doc.Root.GetAttributeBool("usedualmodesockets", true); + DisableInGameHints = doc.Root.GetAttributeBool("disableingamehints", DisableInGameHints); #if DEBUG AutomaticQuickStartEnabled = doc.Root.GetAttributeBool("automaticquickstartenabled", AutomaticQuickStartEnabled); AutomaticCampaignLoadEnabled = doc.Root.GetAttributeBool("automaticcampaignloadenabled", AutomaticCampaignLoadEnabled); TextManagerDebugModeEnabled = doc.Root.GetAttributeBool("textmanagerdebugmodeenabled", TextManagerDebugModeEnabled); + ModBreakerMode = doc.Root.GetAttributeBool("modbreakermode", ModBreakerMode); #endif XElement gameplayElement = doc.Root.Element("gameplay"); jobPreferences = new List>(); @@ -1579,6 +1602,32 @@ namespace Barotrauma CurrentCorePackage = null; enabledRegularPackages.Clear(); +#if DEBUG && CLIENT + if (ModBreakerMode) + { + CurrentCorePackage = ContentPackage.CorePackages.GetRandom(); + foreach (var regularPackage in ContentPackage.RegularPackages) + { + if (Rand.Range(0.0, 1.0) <= 0.5) + { + enabledRegularPackages.Add(regularPackage); + } + } + ContentPackage.SortContentPackages(p => + { + return Rand.Int(int.MaxValue); + }, config: this); + + if (CurrentCorePackage == null) + { + CurrentCorePackage = ContentPackage.CorePackages.First(); + } + + TextManager.LoadTextPacks(AllEnabledPackages); + return; + } +#endif + var contentPackagesElement = doc.Root.Element("contentpackages"); if (contentPackagesElement != null) { @@ -1725,6 +1774,7 @@ namespace Barotrauma AutoUpdateWorkshopItems = true; TextScale = 1; textScaleDirty = false; + DisableInGameHints = false; } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs index ee2a7e91a..71201f648 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/DockingPort.cs @@ -154,7 +154,7 @@ namespace Barotrauma.Items.Components var prevDockingTarget = DockingTarget; Undock(applyEffects: false); Dock(prevDockingTarget); - Lock(true, applyEffects: false); + Lock(isNetworkMessage: true, applyEffects: false); } } @@ -240,7 +240,7 @@ namespace Barotrauma.Items.Components } - public void Lock(bool isNetworkMessage, bool forcePosition = false, bool applyEffects = true) + public void Lock(bool isNetworkMessage, bool applyEffects = true) { #if CLIENT if (GameMain.Client != null && !isNetworkMessage) { return; } @@ -262,20 +262,17 @@ namespace Barotrauma.Items.Components ApplyStatusEffects(ActionType.OnUse, 1.0f); } - if (forcePosition) + Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA; + if (item.Submarine.PhysicsBody.Mass < DockingTarget.item.Submarine.PhysicsBody.Mass || + DockingTarget.item.Submarine.Info.IsOutpost) { - Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA; - if (item.Submarine.PhysicsBody.Mass < DockingTarget.item.Submarine.PhysicsBody.Mass || - DockingTarget.item.Submarine.Info.IsOutpost) - { - item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position + ConvertUnits.ToDisplayUnits(jointDiff)); - } - else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass || - item.Submarine.Info.IsOutpost) - { - DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff)); - } + item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position + ConvertUnits.ToDisplayUnits(jointDiff)); } + else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass || + item.Submarine.Info.IsOutpost) + { + DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff)); + } ConnectWireBetweenPorts(); CreateJoint(true); @@ -988,7 +985,7 @@ namespace Barotrauma.Items.Components } else { - Lock(isNetworkMessage: false, forcePosition: true); + Lock(isNetworkMessage: false); } } else diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs index c7d133f31..ead2a8b54 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs @@ -305,9 +305,21 @@ namespace Barotrauma.Items.Components private void ToggleState(ActionType actionType, Character user) { - if (toggleCooldownTimer > 0.0f && user != lastUser) { OnFailedToOpen(); return; } + if (toggleCooldownTimer > 0.0f && user != lastUser) + { + OnFailedToOpen(); + return; + } toggleCooldownTimer = ToggleCoolDown; - if (IsStuck || IsJammed) { toggleCooldownTimer = 1.0f; OnFailedToOpen(); return; } + if (IsStuck || IsJammed) + { +#if CLIENT + if (IsStuck) { HintManager.OnTryOpenStuckDoor(user); } +#endif + toggleCooldownTimer = 1.0f; + OnFailedToOpen(); + return; + } lastUser = user; SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true, forcedOpen: actionType == ActionType.OnPicked); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs index 0a8abef5a..21d31ac64 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs @@ -12,7 +12,8 @@ namespace Barotrauma.Items.Components { partial class RangedWeapon : ItemComponent { - private float reload, reloadTimer; + private float reload; + public float ReloadTimer { get; private set; } private Vector2 barrelPos; @@ -75,17 +76,17 @@ namespace Barotrauma.Items.Components public override void Equip(Character character) { - reloadTimer = Math.Min(reload, 1.0f); + ReloadTimer = Math.Min(reload, 1.0f); IsActive = true; } public override void Update(float deltaTime, Camera cam) { - reloadTimer -= deltaTime; + ReloadTimer -= deltaTime; - if (reloadTimer < 0.0f) + if (ReloadTimer < 0.0f) { - reloadTimer = 0.0f; + ReloadTimer = 0.0f; IsActive = false; } } @@ -101,10 +102,10 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (character == null || character.Removed) { return false; } - if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || reloadTimer > 0.0f) { return false; } + if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || ReloadTimer > 0.0f) { return false; } IsActive = true; - reloadTimer = reload; + ReloadTimer = reload; if (item.AiTarget != null) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs index 94422a9c9..590429ced 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RepairTool.cs @@ -854,9 +854,11 @@ namespace Barotrauma.Items.Components object value = property.GetValue(target); if (door.Stuck > 0) { + bool isCutting = effect.propertyEffects[i].GetType() == typeof(float) && (float)effect.propertyEffects[i] < 0; var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White, - effect.propertyEffects[i].GetType() == typeof(float) && (float)effect.propertyEffects[i] < 0 ? "progressbar.cutting" : "progressbar.welding"); + textTag: isCutting ? "progressbar.cutting" : "progressbar.welding"); if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); } + if (!isCutting) { HintManager.OnWeldingDoor(user); } } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs index 56b99929b..930bbdf7b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemComponent.cs @@ -771,6 +771,10 @@ namespace Barotrauma.Items.Components brokenEffects.ForEach(e => e.SetUser(user)); } } + +#if CLIENT + HintManager.OnStatusEffectApplied(this, type, character); +#endif } public virtual void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs index b34ccba7e..ba43293af 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs @@ -212,6 +212,7 @@ namespace Barotrauma.Items.Components progressState = 0.0f; timeUntilReady = 0.0f; + UpdateRequiredTimeProjSpecific(); inputContainer.Inventory.Locked = false; outputContainer.Inventory.Locked = false; @@ -279,6 +280,7 @@ namespace Barotrauma.Items.Components if (powerConsumption <= 0) { Voltage = 1.0f; } timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f); + UpdateRequiredTimeProjSpecific(); if (timeUntilReady > 0.0f) { return; } @@ -360,6 +362,8 @@ namespace Barotrauma.Items.Components } } + partial void UpdateRequiredTimeProjSpecific(); + private bool CanBeFabricated(FabricationRecipe fabricableItem) { if (fabricableItem == null) { return false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs index 6aac60622..c59756804 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Reactor.cs @@ -221,6 +221,13 @@ namespace Barotrauma.Items.Components } } +#if CLIENT + if(PowerOn && AvailableFuel < 1) + { + HintManager.OnReactorOutOfFuel(this); + } +#endif + prevAvailableFuel = AvailableFuel; ApplyStatusEffects(ActionType.OnActive, deltaTime, null); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Sonar.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Sonar.cs index 439118dc9..0dc115a10 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Sonar.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Sonar.cs @@ -150,7 +150,7 @@ namespace Barotrauma.Items.Components public override void Update(float deltaTime, Camera cam) { - currPowerConsumption = powerConsumption; + currPowerConsumption = (currentMode == Mode.Active) ? powerConsumption : powerConsumption * 0.1f; UpdateOnActiveEffects(deltaTime); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs index e8ee7805b..3864bd0e0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs @@ -22,6 +22,11 @@ namespace Barotrauma.Items.Components private const float AutoPilotMaxSpeed = 0.5f; private const float AIPilotMaxSpeed = 1.0f; + /// + /// How fast the steering vector adjusts when the nav terminal is operated by something else than a character (= signals) + /// + const float DefaultSteeringAdjustSpeed = 0.2f; + private Vector2 targetVelocity; private Vector2 steeringInput; @@ -543,6 +548,10 @@ namespace Barotrauma.Items.Components { TargetVelocity *= 100.0f / velMagnitude; } + +#if CLIENT + HintManager.OnAutoPilotPathUpdated(this); +#endif } private float? GetNodePenalty(PathNode node, PathNode nextNode) @@ -700,7 +709,10 @@ namespace Barotrauma.Items.Components { if (connection.Name == "velocity_in") { - TargetVelocity = XMLExtensions.ParseVector2(signal, errorMessages: false); + steeringAdjustSpeed = DefaultSteeringAdjustSpeed; + steeringInput = XMLExtensions.ParseVector2(signal, errorMessages: false); + steeringInput.X = MathHelper.Clamp(steeringInput.X, -100.0f, 100.0f); + steeringInput.Y = MathHelper.Clamp(steeringInput.Y, -100.0f, 100.0f); } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/AndComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/AndComponent.cs index 4cfe3b515..8bf81a4b9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/AndComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/AndComponent.cs @@ -4,7 +4,7 @@ using System.Xml.Linq; namespace Barotrauma.Items.Components { class AndComponent : ItemComponent - { + { protected string output, falseOutput; //an array to keep track of how long ago a non-zero signal was received on both inputs @@ -27,14 +27,41 @@ namespace Barotrauma.Items.Components public string Output { get { return output; } - set { output = value; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } } [InGameEditable, Serialize("", true, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)] public string FalseOutput { get { return falseOutput; } - set { falseOutput = value; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } + + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } } public AndComponent(Item item, XElement element) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ColorComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ColorComponent.cs index 55a681c57..80c8ba4d9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ColorComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ColorComponent.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Xml.Linq; +using Microsoft.Xna.Framework; namespace Barotrauma.Items.Components { @@ -10,6 +11,9 @@ namespace Barotrauma.Items.Components private string output = "0,0,0,0"; + [InGameEditable, Serialize(false, true, description: "When enabled makes the component translate the signal from HSV into RGB where red is the hue between 0 and 360, green is the saturation between 0 and 1 and blue is the value between 0 and 1.", alwaysUseInstanceValues: true)] + public bool UseHSV { get; set; } + public ColorComponent(Item item, XElement element) : base(item, element) { @@ -24,10 +28,23 @@ namespace Barotrauma.Items.Components private void UpdateOutput() { - output = receivedSignal[0].ToString("G", CultureInfo.InvariantCulture); - output += "," + receivedSignal[1].ToString("G", CultureInfo.InvariantCulture); - output += "," + receivedSignal[2].ToString("G", CultureInfo.InvariantCulture); - output += "," + receivedSignal[3].ToString("G", CultureInfo.InvariantCulture); + float signalR = receivedSignal[0], + signalG = receivedSignal[1], + signalB = receivedSignal[2], + signalA = receivedSignal[3]; + + if (UseHSV) + { + Color hsvColor = ToolBox.HSVToRGB(signalR, signalG, signalB); + signalR = hsvColor.R / (float) byte.MaxValue; + signalG = hsvColor.G / (float) byte.MaxValue; + signalB = hsvColor.B / (float) byte.MaxValue; + } + + output = signalR.ToString("G", CultureInfo.InvariantCulture); + output += "," + signalG.ToString("G", CultureInfo.InvariantCulture); + output += "," + signalB.ToString("G", CultureInfo.InvariantCulture); + output += "," + signalA.ToString("G", CultureInfo.InvariantCulture); } public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/EqualsComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/EqualsComponent.cs index 6ba37f496..9990b69f7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/EqualsComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/EqualsComponent.cs @@ -15,18 +15,45 @@ namespace Barotrauma.Items.Components //the output is sent if both inputs have received a signal within the timeframe protected float timeFrame; - [InGameEditable, Serialize("1", true, description: "The signal this item outputs when the condition is met.", alwaysUseInstanceValues: true)] + [InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)] public string Output { get { return output; } - set { output = value; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } } - [InGameEditable, Serialize("", true, description: "The signal this item outputs when the condition is not met.", alwaysUseInstanceValues: true)] + [InGameEditable, Serialize("", true, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)] public string FalseOutput { get { return falseOutput; } - set { falseOutput = value; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } + + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } } [InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.", alwaysUseInstanceValues: true)] diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs index ae3627f7e..ce3bae5fa 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/MotionSensor.cs @@ -74,11 +74,48 @@ namespace Barotrauma.Items.Components } } + private string output; [InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)] - public string Output { get; set; } + public string Output + { + get { return output; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } + } + private string falseOutput; [InGameEditable, Serialize("", true, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)] - public string FalseOutput { get; set; } + public string FalseOutput + { + get { return falseOutput; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } + + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } + } [Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)] public float MinimumVelocity diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs index fb0b0ca46..c1de6f086 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/RegExFindComponent.cs @@ -1,4 +1,5 @@ -using System.Text.RegularExpressions; +using System; +using System.Text.RegularExpressions; using System.Xml.Linq; namespace Barotrauma.Items.Components @@ -17,8 +18,22 @@ namespace Barotrauma.Items.Components private bool nonContinuousOutputSent; + private string output; + [InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)] - public string Output { get; set; } + public string Output + { + get { return output; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } + } [InGameEditable, Serialize(false, true, description: "Should the component output a value of a capture group instead of a constant signal.", alwaysUseInstanceValues: true)] public bool UseCaptureGroup { get; set; } @@ -52,6 +67,17 @@ namespace Barotrauma.Items.Components } } + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } + } + public RegExFindComponent(Item item, XElement element) : base(item, element) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SignalCheckComponent.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SignalCheckComponent.cs index cd59dbc7c..c51768670 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SignalCheckComponent.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SignalCheckComponent.cs @@ -1,17 +1,56 @@ -using System.Xml.Linq; +using System; +using System.Xml.Linq; namespace Barotrauma.Items.Components { class SignalCheckComponent : ItemComponent { + private string output; [InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.", alwaysUseInstanceValues: true)] - public string Output { get; set; } + public string Output + { + get { return output; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } + } + + private string falseOutput; [InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the target signal.", alwaysUseInstanceValues: true)] - public string FalseOutput { get; set; } + public string FalseOutput + { + get { return falseOutput; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } [InGameEditable, Serialize("", true, description: "The value to compare the received signals against.", alwaysUseInstanceValues: true)] public string TargetSignal { get; set; } + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } + } + public SignalCheckComponent(Item item, XElement element) : base(item, element) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SmokeDetector.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SmokeDetector.cs index 7f71626c7..c78a00b28 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SmokeDetector.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/SmokeDetector.cs @@ -1,4 +1,5 @@ -using System.Xml.Linq; +using System; +using System.Xml.Linq; namespace Barotrauma.Items.Components { @@ -9,11 +10,48 @@ namespace Barotrauma.Items.Components private bool fireInRange; - [InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)] - public string Output { get; set; } + private string output; + [InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected a fire.", alwaysUseInstanceValues: true)] + public string Output + { + get { return output; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } + } - [InGameEditable, Serialize("0", true, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)] - public string FalseOutput { get; set; } + private string falseOutput; + [InGameEditable, Serialize("0", true, description: "The signal the item outputs when it has not detected a fire.", alwaysUseInstanceValues: true)] + public string FalseOutput + { + get { return falseOutput; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } + + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } + } public SmokeDetector(Item item, XElement element) : base(item, element) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WaterDetector.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WaterDetector.cs index 3ae59f88d..c33d23331 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WaterDetector.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/WaterDetector.cs @@ -12,11 +12,48 @@ namespace Barotrauma.Items.Components private bool isInWater; private float stateSwitchDelay; + private string output; [InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.", alwaysUseInstanceValues: true)] - public string Output { get; set; } + public string Output + { + get { return output; } + set + { + if (value == null) { return; } + output = value; + if (output.Length > MaxOutputLength) + { + output = output.Substring(0, MaxOutputLength); + } + } + } + private string falseOutput; [InGameEditable, Serialize("0", true, description: "The signal the item sends out when it's not underwater.", alwaysUseInstanceValues: true)] - public string FalseOutput { get; set; } + public string FalseOutput + { + get { return falseOutput; } + set + { + if (value == null) { return; } + falseOutput = value; + if (falseOutput.Length > MaxOutputLength) + { + falseOutput = falseOutput.Substring(0, MaxOutputLength); + } + } + } + + private int maxOutputLength; + [Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")] + public int MaxOutputLength + { + get { return maxOutputLength; } + set + { + maxOutputLength = Math.Max(value, 0); + } + } public WaterDetector(Item item, XElement element) : base(item, element) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs index 6309d6935..66bd4320d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs @@ -767,7 +767,6 @@ namespace Barotrauma.Items.Components TryLaunch(deltaTime, ignorePower: true); } - private bool outOfAmmo; public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && @@ -834,7 +833,7 @@ namespace Barotrauma.Items.Components } if (container == null || container.ContainableItems.Count == 0) { - if (!outOfAmmo && character.IsOnPlayerTeam) + if (character.IsOnPlayerTeam) { character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "cannotloadturret", 30.0f); } @@ -850,7 +849,7 @@ namespace Barotrauma.Items.Components } loadItemsObjective.Abandoned += CheckRemainingAmmo; loadItemsObjective.Completed += CheckRemainingAmmo; - return outOfAmmo; + return false; void CheckRemainingAmmo() { @@ -1203,8 +1202,7 @@ namespace Barotrauma.Items.Components resetUserTimer = 10.0f; break; case "trigger_in": - if (signal == "0") { return; } - lightComponent.IsOn = !lightComponent.IsOn; + if (signal == "0") { return; } item.Use((float)Timing.Step, sender); user = sender; resetUserTimer = 10.0f; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs index 2ad634ba9..be7a89c9d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs @@ -826,7 +826,8 @@ namespace Barotrauma { slots[index].Add(item); item.ParentInventory = this; - if (item.body != null) + bool equipped = (this as CharacterInventory)?.Owner is Character character && character.HasEquippedItem(item); + if (item.body != null && !equipped) { item.body.Enabled = false; item.body.BodyType = FarseerPhysics.BodyType.Dynamic; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs index 499f84a9b..aeffbd4a6 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs @@ -485,12 +485,14 @@ namespace Barotrauma public int ClusterQuantity { get; } public int ClusterSize { get; } public bool IsIslandSpecifc { get; } + public bool AllowAtStart { get; } - public FixedQuantityResourceInfo(int clusterQuantity, int clusterSize, bool isIslandSpecific) + public FixedQuantityResourceInfo(int clusterQuantity, int clusterSize, bool isIslandSpecific, bool allowAtStart) { ClusterQuantity = clusterQuantity; ClusterSize = clusterSize; IsIslandSpecifc = isIslandSpecific; + AllowAtStart = allowAtStart; } } @@ -965,7 +967,8 @@ namespace Barotrauma LevelQuantity.Add(levelName, new FixedQuantityResourceInfo( levelCommonnessElement.GetAttributeInt("clusterquantity", 0), levelCommonnessElement.GetAttributeInt("clustersize", 0), - levelCommonnessElement.GetAttributeBool("isislandspecific", false))); + levelCommonnessElement.GetAttributeBool("isislandspecific", false), + levelCommonnessElement.GetAttributeBool("allowatstart", true))); } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs index 2c9962767..c64e7cd84 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs @@ -159,6 +159,11 @@ namespace Barotrauma get { return AbyssArea.Y + AbyssArea.Height; } } + public int AbyssEnd + { + get { return AbyssArea.Y; } + } + public class AbyssIsland { public readonly Rectangle Area; @@ -1370,15 +1375,15 @@ namespace Barotrauma float seaFloorPos = GetBottomPosition(xPos).Y; //above the bottom of the level = can't place a point here - if (seaFloorPos > AbyssArea.Bottom) { continue; } + if (seaFloorPos > AbyssStart) { continue; } - float yPos = Rand.Range(Math.Max(seaFloorPos, AbyssArea.Y), AbyssArea.Bottom); + float yPos = MathHelper.Lerp(AbyssStart, Math.Max(seaFloorPos, AbyssArea.Y), Rand.Range(0.2f, 1.0f, Rand.RandSync.Server)); foreach (var abyssIsland in AbyssIslands) { if (abyssIsland.Area.Contains(new Point((int)xPos, (int)yPos))) { - xPos = abyssIsland.Area.Center.X + (int)(Rand.Int(1) == 0 ? abyssIsland.Area.Width * -0.6f : 0.6f); + xPos = abyssIsland.Area.Center.X + (int)(Rand.Int(1, Rand.RandSync.Server) == 0 ? abyssIsland.Area.Width * -0.6f : 0.6f); } } @@ -2035,6 +2040,7 @@ namespace Barotrauma { if (l.Cell == null || l.Edge == null) { return false; } if (resourceInfo.IsIslandSpecifc && !l.Cell.Island) { return false; } + if (!resourceInfo.AllowAtStart && l.EdgeCenter.Y > StartPosition.Y && l.EdgeCenter.X < Size.X * 0.25f) { return false; } if (l.EdgeCenter.Y < AbyssArea.Bottom) { return false; } return resourceInfo.ClusterSize <= GetMaxResourcesOnEdge(itemPrefab, l, out _); @@ -3240,13 +3246,13 @@ namespace Barotrauma } #endif } - if (StartLocation != null && !StartLocation.HasOutpost()) { continue; } + if (StartLocation != null && !StartLocation.Type.HasOutpost) { continue; } } else { //don't create an end outpost for locations if (LevelData.Type == LevelData.LevelType.Outpost) { continue; } - if (EndLocation != null && !EndLocation.HasOutpost()) { continue; } + if (EndLocation != null && !EndLocation.Type.HasOutpost) { continue; } } SubmarineInfo outpostInfo; @@ -3266,7 +3272,7 @@ namespace Barotrauma { var suitableParams = OutpostGenerationParams.Params .Where(p => location == null || p.AllowedLocationTypes.Contains(location.Type.Identifier)); - if (suitableParams.Count() == 0) + if (!suitableParams.Any()) { suitableParams = OutpostGenerationParams.Params .Where(p => location == null || !p.AllowedLocationTypes.Any()); @@ -3305,7 +3311,7 @@ namespace Barotrauma foreach (string categoryToHide in locationType.HideEntitySubcategories) { - foreach (MapEntity entityToHide in MapEntity.mapEntityList.Where(me => me.Submarine == outpost && me.prefab.HasSubCategory(categoryToHide))) + foreach (MapEntity entityToHide in MapEntity.mapEntityList.Where(me => me.Submarine == outpost && (me.prefab?.HasSubCategory(categoryToHide) ?? false))) { entityToHide.HiddenInGame = true; } @@ -3444,7 +3450,12 @@ namespace Barotrauma Item reactorItem = beaconItems.Find(it => it.GetComponent() != null); Reactor reactorComponent = reactorItem.GetComponent(); ItemContainer reactorContainer = reactorItem.GetComponent(); - + Repairable repairable = reactorItem.GetComponent(); + reactorComponent.FuelConsumptionRate = 0.0f; + if (repairable != null) + { + repairable.DeteriorationSpeed = 0.0f; + } if (LevelData.IsBeaconActive) { if (reactorContainer.Inventory.IsEmpty()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs index ac4c95bc1..b0d960867 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/LinkedSubmarine.cs @@ -236,6 +236,11 @@ namespace Barotrauma DebugConsole.ThrowError("Failed to load a linked submarine (empty XML element). The save file may be corrupted."); return; } + if (!info.SubmarineElement.Elements().Any(e => e.Name.ToString().Equals("hull", StringComparison.OrdinalIgnoreCase))) + { + DebugConsole.ThrowError("Failed to load a linked submarine (the submarine contains no hulls)."); + return; + } IdRemap parentRemap = new IdRemap(Submarine.Info.SubmarineElement, Submarine.IdOffset); sub = Submarine.Load(info, false, parentRemap); @@ -322,7 +327,7 @@ namespace Barotrauma sub.SetPosition((linkedPort.Item.WorldPosition - portDiff) - offset); myPort.Dock(linkedPort); - myPort.Lock(true, applyEffects: false); + myPort.Lock(isNetworkMessage: true, applyEffects: false); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs index 0ded8788e..f842aedbd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs @@ -579,6 +579,16 @@ namespace Barotrauma return false; } + public LocationType GetLocationType() + { + if (IsCriticallyRadiated() && LocationType.List.FirstOrDefault(lt => lt.Identifier.Equals(Type.ReplaceInRadiation, StringComparison.OrdinalIgnoreCase)) is { } newLocationType) + { + return newLocationType; + } + + return Type; + } + public IEnumerable GetMissionsInConnection(LocationConnection connection) { System.Diagnostics.Debug.Assert(Connections.Contains(connection)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationConnection.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationConnection.cs index d27827f70..18a370d32 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationConnection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationConnection.cs @@ -14,6 +14,8 @@ namespace Barotrauma public bool Passed; + public bool Locked; + public LevelData LevelData { get; set; } public Vector2 CenterPos diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs index c7dfceca6..d4a2d57f0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/LocationType.cs @@ -54,8 +54,11 @@ namespace Barotrauma get; private set; } + + public string ReplaceInRadiation { get; } public Sprite Sprite { get; private set; } + public Sprite RadiationSprite { get; } public Color SpriteColor { @@ -85,6 +88,8 @@ namespace Barotrauma HideEntitySubcategories = element.GetAttributeStringArray("hideentitysubcategories", new string[0]).ToList(); + ReplaceInRadiation = element.GetAttributeString(nameof(ReplaceInRadiation).ToLower(), ""); + string nameFile = element.GetAttributeString("namefile", "Content/Map/locationNames.txt"); try { @@ -140,6 +145,9 @@ namespace Barotrauma Sprite = new Sprite(subElement, lazyLoad: true); SpriteColor = subElement.GetAttributeColor("color", Color.White); break; + case "radiationsymbol": + RadiationSprite = new Sprite(subElement, lazyLoad: true); + break; case "changeto": CanChangeTo.Add(new LocationTypeChange(Identifier, subElement, requireChangeMessages: true)); break; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs index 5356ffd92..5eaddfa6b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs @@ -59,18 +59,21 @@ namespace Barotrauma public Radiation Radiation; - public Map() + public Map(CampaignSettings settings) { generationParams = MapGenerationParams.Instance; Locations = new List(); Connections = new List(); - Radiation = new Radiation(this, generationParams.RadiationParams); + Radiation = new Radiation(this, generationParams.RadiationParams) + { + Enabled = settings.RadiationEnabled + }; } /// /// Load a previously saved campaign map from XML /// - private Map(CampaignMode campaign, XElement element) : this() + private Map(CampaignMode campaign, XElement element, CampaignSettings settings) : this(settings) { Seed = element.GetAttributeString("seed", "a"); Rand.SetSyncedSeed(ToolBox.StringToInt(Seed)); @@ -87,7 +90,10 @@ namespace Barotrauma Locations[i] = new Location(subElement); break; case "radiation": - Radiation = new Radiation(this, generationParams.RadiationParams, subElement); + Radiation = new Radiation(this, generationParams.RadiationParams, subElement) + { + Enabled = settings.RadiationEnabled + }; break; } } @@ -107,6 +113,7 @@ namespace Barotrauma var connection = new LocationConnection(Locations[locationIndices.X], Locations[locationIndices.Y]) { Passed = subElement.GetAttributeBool("passed", false), + Locked = subElement.GetAttributeBool("locked", false), Difficulty = subElement.GetAttributeFloat("difficulty", 0.0f) }; Locations[locationIndices.X].Connections.Add(connection); @@ -162,7 +169,7 @@ namespace Barotrauma /// /// Generate a new campaign map from the seed /// - public Map(CampaignMode campaign, string seed) : this() + public Map(CampaignMode campaign, string seed, CampaignSettings settings) : this(settings) { Seed = seed; Rand.SetSyncedSeed(ToolBox.StringToInt(Seed)); @@ -404,6 +411,7 @@ namespace Barotrauma leftMostLocation.ChangeType(LocationType.List.First(lt => lt.HasOutpost)); } leftMostLocation.IsGateBetweenBiomes = true; + Connections[i].Locked = true; } } @@ -688,6 +696,10 @@ namespace Barotrauma SelectedConnection = Connections.Find(c => c.Locations.Contains(GameMain.GameSession?.Campaign?.CurrentDisplayLocation) && c.Locations.Contains(SelectedLocation)) ?? Connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation)); + if (SelectedConnection?.Locked ?? false) + { + DebugConsole.ThrowError("A locked connection was selected - this should not be possible.\n" + Environment.StackTrace.CleanupStackTrace()); + } OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection); } @@ -703,6 +715,10 @@ namespace Barotrauma SelectedLocation = location; SelectedConnection = Connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation)); + if (SelectedConnection?.Locked ?? false) + { + DebugConsole.ThrowError("A locked connection was selected - this should not be possible.\n" + Environment.StackTrace.CleanupStackTrace()); + } OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection); } @@ -736,7 +752,7 @@ namespace Barotrauma public void SelectRandomLocation(bool preferUndiscovered) { - List nextLocations = CurrentLocation.Connections.Select(c => c.OtherLocation(CurrentLocation)).ToList(); + List nextLocations = CurrentLocation.Connections.Where(c => !c.Locked).Select(c => c.OtherLocation(CurrentLocation)).ToList(); List undiscoveredLocations = nextLocations.FindAll(l => !l.Discovered); if (undiscoveredLocations.Count > 0 && preferUndiscovered) @@ -943,9 +959,9 @@ namespace Barotrauma /// /// Load a previously saved map from an xml element /// - public static Map Load(CampaignMode campaign, XElement element) + public static Map Load(CampaignMode campaign, XElement element, CampaignSettings settings) { - Map map = new Map(campaign, element); + Map map = new Map(campaign, element, settings); map.LoadState(element, false); #if CLIENT map.DrawOffset = -map.CurrentLocation.MapPosition; @@ -1079,6 +1095,7 @@ namespace Barotrauma var connectionElement = new XElement("connection", new XAttribute("passed", connection.Passed), + new XAttribute("locked", connection.Locked), new XAttribute("difficulty", connection.Difficulty), new XAttribute("biome", connection.Biome.Identifier), new XAttribute("locations", Locations.IndexOf(connection.Locations[0]) + "," + Locations.IndexOf(connection.Locations[1]))); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Radiation.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Radiation.cs index 332223772..4fe8770e4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Radiation.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/Radiation.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; @@ -13,6 +14,9 @@ namespace Barotrauma [Serialize(defaultValue: 0f, isSaveable: true)] public float Amount { get; set; } + [Serialize(defaultValue: true, isSaveable: true)] + public bool Enabled { get; set; } + public Dictionary SerializableProperties { get; } public readonly Map Map; @@ -23,8 +27,6 @@ namespace Barotrauma private float increasedAmount; private float lastIncrease; - public bool Enabled = true; - public Radiation(Map map, RadiationParams radiationParams, XElement? element = null) { SerializableProperties = SerializableProperty.DeserializeProperties(this, element); @@ -52,6 +54,12 @@ namespace Barotrauma foreach (Location location in Map.Locations.Where(Contains)) { + if (location.IsGateBetweenBiomes) + { + location.Connections.ForEach(c => c.Locked = false); + continue; + } + if (amountOfOutposts <= Params.MinimumOutpostAmount) { break; } if (Map.CurrentLocation is { } currLocation) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/RadiationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/RadiationParams.cs index b806a2f69..8e5cbc540 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Map/RadiationParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Map/RadiationParams.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Xml.Linq; +using Microsoft.Xna.Framework; namespace Barotrauma { @@ -29,6 +30,15 @@ namespace Barotrauma [Serialize(defaultValue: 1f, isSaveable: false, "How much is the radiation affliction increased by while in a radiated zone.")] public float RadiationDamageAmount { get; set; } + [Serialize(defaultValue: "139,0,0,85", isSaveable: false, "The color of the radiated area.")] + public Color RadiationAreaColor { get; set; } + + [Serialize(defaultValue: "255,0,0,255", isSaveable: false, "The tint of the radiation border sprites.")] + public Color RadiationBorderTint { get; set; } + + [Serialize(defaultValue: 16.66f, isSaveable: false, "Speed of the border spritesheet animation.")] + public float BorderAnimationSpeed { get; set; } + public RadiationParams(XElement element) { SerializableProperties = SerializableProperty.DeserializeProperties(this, element); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs index e7f868b20..577750efd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerationParams.cs @@ -67,6 +67,9 @@ namespace Barotrauma set; } + [Serialize("", isSaveable: true), Editable] + public string ReplaceInRadiation { get; set; } + private readonly Dictionary moduleCounts = new Dictionary(); public IEnumerable> ModuleCounts diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs index 20c8c4acf..47c589c0b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs @@ -68,6 +68,15 @@ namespace Barotrauma private static Submarine Generate(OutpostGenerationParams generationParams, LocationType locationType, Location location, bool onlyEntrance = false) { var outpostModuleFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.OutpostModule); + if (location != null) + { + if (location.IsCriticallyRadiated() && OutpostGenerationParams.Params.FirstOrDefault(p => p.Identifier.Equals(generationParams.ReplaceInRadiation, StringComparison.OrdinalIgnoreCase)) is { } newParams) + { + generationParams = newParams; + } + + locationType = location.GetLocationType(); + } //load the infos of the outpost module files List outpostModules = new List(); @@ -974,7 +983,7 @@ namespace Barotrauma var moduleEntities = MapEntity.LoadAll(sub, hallwayInfo.SubmarineElement, hallwayInfo.FilePath, -1); //remove items that don't fit in the hallway - moduleEntities.Where(e => e is Item item && item.GetComponent() == null && e.Rect.Width > hallwayLength).ForEach(e => e.Remove()); + moduleEntities.Where(e => e is Item item && item.GetComponent() == null && (isHorizontal ? e.Rect.Width : e.Rect.Height) > hallwayLength).ForEach(e => e.Remove()); //find the largest hull to use it as the center point of the hallway //and the bounds of all the hulls, used when resizing the hallway to fit between the modules @@ -1047,11 +1056,11 @@ namespace Barotrauma } } } - else if (me is Structure structure) + else if (me is Structure || (me is Item item && item.GetComponent() == null)) { if (isHorizontal) { - if (!structure.ResizeHorizontal) + if (!me.ResizeHorizontal) { int xPos = (int)(leftHull.WorldRect.Right + (me.WorldPosition.X - hullBounds.X) * scaleFactor); me.Rect = new Rectangle(xPos - me.RectWidth / 2, me.Rect.Y, me.Rect.Width, me.Rect.Height); @@ -1065,9 +1074,9 @@ namespace Barotrauma } else { - if (!structure.ResizeVertical) + if (!me.ResizeVertical) { - int yPos = (int)(topHull.WorldRect.Y - topHull.RectHeight + (me.WorldPosition.X - hullBounds.Bottom) * scaleFactor); + int yPos = (int)(topHull.WorldRect.Y - topHull.RectHeight + (me.WorldPosition.Y - hullBounds.Bottom) * scaleFactor); me.Rect = new Rectangle(me.Rect.X, yPos + me.RectHeight / 2, me.Rect.Width, me.Rect.Height); } else @@ -1394,7 +1403,7 @@ namespace Barotrauma ISpatialEntity gotoTarget = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Human, humanPrefab.GetModuleFlags(), humanPrefab.GetSpawnPointTags()); if (gotoTarget == null) { - gotoTarget = outpost.GetHulls(true).GetRandom(); + gotoTarget = outpost.GetHulls(true).GetRandom(Rand.RandSync.Server); } characterInfo.TeamID = CharacterTeamType.FriendlyNPC; var npc = Character.Create(CharacterPrefab.HumanConfigFile, SpawnAction.OffsetSpawnPos(gotoTarget.WorldPosition, 100.0f), ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs index 8b43bc834..b7dcaa057 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs @@ -590,10 +590,11 @@ namespace Barotrauma if (item.GetComponent() != null) { return false; } if (item.body != null && !item.body.Enabled) { return true; } } + if (e.HiddenInGame) { return true; } return false; }); - if (entities.Count == 0) return Rectangle.Empty; + if (entities.Count == 0) { return Rectangle.Empty; } float minX = entities[0].Rect.X, minY = entities[0].Rect.Y - entities[0].Rect.Height; float maxX = entities[0].Rect.Right, maxY = entities[0].Rect.Y; @@ -1356,7 +1357,7 @@ namespace Barotrauma if (me.Submarine != this) { continue; } if (me is Item item) { - item.SpawnedInOutpost = !info.OutpostGenerationParams.AllowStealing; + item.SpawnedInOutpost = info.OutpostGenerationParams != null && !info.OutpostGenerationParams.AllowStealing; if (item.GetComponent() != null && indestructible) { item.Indestructible = true; @@ -1366,7 +1367,7 @@ namespace Barotrauma if (ic is ConnectionPanel connectionPanel) { //prevent rewiring - if (!info.OutpostGenerationParams.AlwaysRewireable) + if (info.OutpostGenerationParams != null && !info.OutpostGenerationParams.AlwaysRewireable) { connectionPanel.Locked = true; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs index 633aec372..1f6f0a56e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs @@ -11,7 +11,9 @@ using Barotrauma.Extensions; namespace Barotrauma { - public enum SpawnType { Path = 0, Human = 1, Enemy = 2, Cargo = 3, Corpse = 4 }; + [Flags] + public enum SpawnType { Path = 0, Human = 1, Enemy = 2, Cargo = 4, Corpse = 8 }; + partial class WayPoint : MapEntity { public static List WayPointList = new List(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs index 9a4c83272..332de65d0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Client.cs @@ -13,6 +13,7 @@ namespace Barotrauma.Networking public string Name; public UInt16 NameID; public byte ID; public UInt64 SteamID; + public UInt64 OwnerSteamID; public string Language; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/NetworkConnection.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/NetworkConnection.cs index 4ddf29479..0c1569709 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/NetworkConnection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/NetworkConnection.cs @@ -21,6 +21,12 @@ namespace Barotrauma.Networking protected set; } + public UInt64 OwnerSteamID + { + get; + protected set; + } + public string EndPointString { get; @@ -44,5 +50,15 @@ namespace Barotrauma.Networking //is received by the server. return false; } + + public bool SetOwnerSteamIDIfUnknown(UInt64 id) + { + //we know that for both Lidgren and SteamP2P, the + //owner id isn't known until the auth ticket is + //processed, so this method is the same for both + if (OwnerSteamID != 0) { return false; } + OwnerSteamID = id; + return true; + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs index a65506196..7e70ad98b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/Primitives/NetworkConnection/SteamP2PConnection.cs @@ -10,6 +10,7 @@ namespace Barotrauma.Networking public SteamP2PConnection(string name, UInt64 steamId) { SteamID = steamId; + OwnerSteamID = 0; EndPointString = SteamManager.SteamIDUInt64ToString(SteamID); Name = name; Heartbeat(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs index 8196a5495..d2a013e7a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs @@ -872,6 +872,13 @@ namespace Barotrauma.Networking private set; } + [Serialize(true, true)] + public bool RadiationEnabled + { + get; + set; + } + public void SetPassword(string password) { if (string.IsNullOrEmpty(password)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IPrefab.cs index 37cf1891d..d2b2e9d27 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/IPrefab.cs @@ -30,7 +30,7 @@ namespace Barotrauma var collision = prefabs.Find(p => p != prefab && p.UIntIdentifier == prefab.UIntIdentifier); if (collision != null) { - DebugConsole.ThrowError($"Hashing collision when generating uint identifiers for {nameof(T)}: {prefab.Identifier} has the same identifier as {collision.Identifier} ({prefab.UIntIdentifier})"); + DebugConsole.ThrowError($"Hashing collision when generating uint identifiers for {typeof(T).Name}: {prefab.Identifier} has the same identifier as {collision.Identifier} ({prefab.UIntIdentifier})"); collision.UIntIdentifier++; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs index 62839c061..86eababf0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabCollection.cs @@ -93,7 +93,7 @@ namespace Barotrauma //Handle bad overrides and duplicates if (basePrefabExists && !isOverride) { - DebugConsole.ThrowError($"Error registering \"{prefab.OriginalName}\", \"{prefab.Identifier}\" ({typeof(T).ToString()}): base already exists; try overriding"); + DebugConsole.ThrowError($"Error registering \"{prefab.OriginalName}\", \"{prefab.Identifier}\" ({typeof(T).ToString()}): base already exists; try overriding\n{Environment.StackTrace}"); return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/NetLobbyScreen.cs index 4d5cfa806..8f10e2d5b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/NetLobbyScreen.cs @@ -36,6 +36,22 @@ namespace Barotrauma levelDifficultyScrollBar.OnMoved(levelDifficultyScrollBar, levelDifficultyScrollBar.BarScroll); #endif } + + public void SetRadiationEnabled(bool enabled) + { +#if CLIENT + radiationEnabledTickBox.Selected = enabled; +#endif + } + + public bool IsRadiationEnabled() + { +#if CLIENT + return radiationEnabledTickBox.Selected; +#elif SERVER + return GameMain.Server.ServerSettings.RadiationEnabled; +#endif + } public void ToggleTraitorsEnabled(int dir) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs index 0a21bcdce..fb24f1a63 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Serialization/XMLExtensions.cs @@ -570,6 +570,25 @@ namespace Barotrauma public static Color ParseColor(string stringColor, bool errorMessages = true) { + if (stringColor.StartsWith("gui.", StringComparison.OrdinalIgnoreCase)) + { +#if CLIENT + if (GUI.Style != null) + { + string colorName = stringColor.Substring(4); + var property = GUI.Style.GetType().GetProperties().FirstOrDefault( + p => p.PropertyType == typeof(Color) && + p.Name.Equals(colorName, StringComparison.OrdinalIgnoreCase)); + if (property != null) + { + return (Color)property?.GetValue(GUI.Style); + } + } +#endif + return Color.White; + } + + string[] strComponents = stringColor.Split(','); Color color = Color.White; diff --git a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs index 091e62f5c..e9749833e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs @@ -39,6 +39,66 @@ namespace Barotrauma } } + class AITrigger : ISerializableEntity + { + public string Name => "ai trigger"; + + public Dictionary SerializableProperties { get; set; } + + [Serialize(AIState.Idle, false)] + public AIState State { get; private set; } + + [Serialize(0f, false)] + public float Duration { get; private set; } + + [Serialize(1f, false)] + public float Probability { get; private set; } + + [Serialize(0f, false)] + public float MinDamage { get; private set; } + + [Serialize(true, false)] + public bool AllowToOverride { get; private set; } + + [Serialize(true, false)] + public bool AllowToBeOverridden { get; private set; } + + public bool IsTriggered { get; private set; } + + public float Timer { get; private set; } = -1; + + public bool IsActive { get; private set; } + + public void Launch() + { + IsTriggered = true; + IsActive = true; + Timer = Duration; + } + + public void Reset() + { + IsTriggered = false; + IsActive = false; + Timer = 0; + } + + public void UpdateTimer(float deltaTime) + { + Timer -= deltaTime; + if (Timer < 0) + { + Timer = 0; + IsActive = false; + } + } + + public AITrigger(XElement element) + { + SerializableProperties = SerializableProperty.DeserializeProperties(this, element); + } + } + partial class StatusEffect { [Flags] @@ -203,6 +263,7 @@ namespace Barotrauma private readonly List spawnItems; private readonly List spawnCharacters; + private readonly List aiTriggers; private readonly List triggeredEvents; private readonly string triggeredEventTargetTag = "statuseffecttarget", @@ -282,6 +343,7 @@ namespace Barotrauma requiredItems = new List(); spawnItems = new List(); spawnCharacters = new List(); + aiTriggers = new List(); Afflictions = new List(); Explosions = new List(); triggeredEvents = new List(); @@ -532,7 +594,6 @@ namespace Barotrauma triggeredEvents.Add(prefab); } } - foreach (XElement eventElement in subElement.Elements()) { if (!eventElement.Name.ToString().Equals("ScriptedEvent", StringComparison.OrdinalIgnoreCase)) { continue; } @@ -543,6 +604,9 @@ namespace Barotrauma var newSpawnCharacter = new CharacterSpawnInfo(subElement, parentDebugName); if (!string.IsNullOrWhiteSpace(newSpawnCharacter.SpeciesName)) { spawnCharacters.Add(newSpawnCharacter); } break; + case "aitrigger": + aiTriggers.Add(new AITrigger(subElement)); + break; } } InitProjSpecific(element, parentDebugName); @@ -1032,7 +1096,7 @@ namespace Barotrauma { targetCharacter = character; } - else if (target is Limb limb) + else if (target is Limb limb && !limb.Removed) { targetLimb = limb; targetCharacter = limb.character; @@ -1053,6 +1117,32 @@ namespace Barotrauma #endif } } + + if (aiTriggers.Any()) + { + Character targetCharacter = target as Character; + if (targetCharacter == null) + { + if (target is Limb targetLimb && !targetLimb.Removed) + { + targetCharacter = targetLimb.character; + } + } + if (targetCharacter != null && !targetCharacter.Removed && !targetCharacter.IsPlayer) + { + if (targetCharacter.AIController is EnemyAIController enemyAI) + { + foreach (AITrigger trigger in aiTriggers) + { + if (Rand.Value(Rand.RandSync.Unsynced) > trigger.Probability) { continue; } + if (target is Limb targetLimb && targetCharacter.LastDamage.HitLimb != targetLimb) { continue; } + if (targetCharacter.LastDamage.Damage < trigger.MinDamage) { continue; } + enemyAI.LaunchTrigger(trigger); + break; + } + } + } + } } if (FireSize > 0.0f && entity != null) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/MathUtils.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/MathUtils.cs index d52c654f3..299e6d6dd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/MathUtils.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/MathUtils.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Barotrauma.Extensions; +using System.Linq; namespace Barotrauma { @@ -1066,6 +1067,16 @@ namespace Barotrauma if (diff == 0) { return v >= max ? 1f : 0f; } return MathHelper.Clamp((v - min) / diff, 0f, 1f); } + + public static float Min(params float[] vals) + { + return vals.Min(); + } + + public static float Max(params float[] vals) + { + return vals.Max(); + } } class CompareCCW : IComparer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs index fb5ae54bc..201c9d7e9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/SafeIO.cs @@ -7,7 +7,10 @@ namespace Barotrauma.IO { static readonly string[] unwritableDirs = new string[] { "Content", "Data/ContentPackages" }; - public static bool DevException; + /// + /// When set to true, the game is allowed to modify the vanilla content in debug builds. Has no effect in non-debug builds. + /// + public static bool SkipValidationInDebugBuilds; public static bool CanWrite(string path) { @@ -20,7 +23,7 @@ namespace Barotrauma.IO if (path.StartsWith(dir, StringComparison.InvariantCultureIgnoreCase)) { #if DEBUG - return DevException; + return SkipValidationInDebugBuilds; #else return false; #endif @@ -37,7 +40,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot save XML document to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot save XML document to \"{path}\": modifying the files in the folder is not allowed."); return; } doc.Save(path); @@ -47,7 +50,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot save XML element to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot save XML element to \"{path}\": modifying the files in the folder is not allowed."); return; } element.Save(path); @@ -72,7 +75,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot write XML document to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot write XML document to \"{path}\": modifying the files in the folder is not allowed."); Writer = null; return; } @@ -222,7 +225,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot create directory \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot create directory \"{path}\": modifying the contents of the folder is not allowed."); return null; } return System.IO.Directory.CreateDirectory(path); @@ -232,7 +235,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot delete directory \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot delete directory \"{path}\": modifying the contents of the folder is not allowed."); return; } //TODO: validate recursion? @@ -251,7 +254,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(dest)) { - DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": failed validation"); + DebugConsole.ThrowError($"Cannot copy \"{src}\" to \"{dest}\": modifying the contents of the folder is not allowed."); return; } System.IO.File.Copy(src, dest, overwrite); @@ -261,12 +264,12 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(src)) { - DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": src failed validation"); + DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the source folder is not allowed."); return; } if (!Validation.CanWrite(dest)) { - DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": dest failed validation"); + DebugConsole.ThrowError($"Cannot move \"{src}\" to \"{dest}\": modifying the contents of the destination folder is not allowed"); return; } System.IO.File.Move(src, dest); @@ -276,7 +279,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot delete file \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot delete file \"{path}\": modifying the contents of the folder is not allowed."); return; } System.IO.File.Delete(path); @@ -298,7 +301,7 @@ namespace Barotrauma.IO case System.IO.FileMode.Truncate: if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot open \"{path}\" in {mode} mode: failed validation"); + DebugConsole.ThrowError($"Cannot open \"{path}\" in {mode} mode: modifying the contents of the folder is not allowed."); return null; } break; @@ -328,7 +331,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot write all bytes to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot write all bytes to \"{path}\": modifying the files in the folder is not allowed."); return; } System.IO.File.WriteAllBytes(path, contents); @@ -338,7 +341,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot write all text to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot write all text to \"{path}\": modifying the files in the folder is not allowed."); return; } System.IO.File.WriteAllText(path, contents, encoding ?? System.Text.Encoding.UTF8); @@ -348,7 +351,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(path)) { - DebugConsole.ThrowError($"Cannot write all lines to \"{path}\": failed validation"); + DebugConsole.ThrowError($"Cannot write all lines to \"{path}\": modifying the files in the folder is not allowed."); return; } System.IO.File.WriteAllLines(path, contents, encoding ?? System.Text.Encoding.UTF8); @@ -420,7 +423,7 @@ namespace Barotrauma.IO } else { - DebugConsole.ThrowError($"Cannot write to file \"{fileName}\": failed validation"); + DebugConsole.ThrowError($"Cannot write to file \"{fileName}\": modifying the files in the folder is not allowed."); } } @@ -487,7 +490,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(innerInfo.FullName)) { - DebugConsole.ThrowError($"Cannot delete directory \"{Name}\": failed validation"); + DebugConsole.ThrowError($"Cannot delete directory \"{Name}\": modifying the contents of the folder is not allowed."); return; } innerInfo.Delete(); @@ -523,7 +526,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(innerInfo.FullName)) { - DebugConsole.ThrowError($"Cannot set read-only to {value} for \"{Name}\": failed validation"); + DebugConsole.ThrowError($"Cannot set read-only to {value} for \"{Name}\": modifying the files in the folder is not allowed."); return; } innerInfo.IsReadOnly = value; @@ -534,7 +537,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(dest)) { - DebugConsole.ThrowError($"Cannot copy \"{Name}\" to \"{dest}\": failed validation"); + DebugConsole.ThrowError($"Cannot copy \"{Name}\" to \"{dest}\": modifying the contents of the destination folder is not allowed."); return; } innerInfo.CopyTo(dest, overwriteExisting); @@ -544,7 +547,7 @@ namespace Barotrauma.IO { if (!Validation.CanWrite(innerInfo.FullName)) { - DebugConsole.ThrowError($"Cannot delete file \"{Name}\": failed validation"); + DebugConsole.ThrowError($"Cannot delete file \"{Name}\": modifying the files in the folder is not allowed."); return; } innerInfo.Delete(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Utils/ToolBox.cs b/Barotrauma/BarotraumaShared/SharedSource/Utils/ToolBox.cs index 2d946aa2f..1d68cccc5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Utils/ToolBox.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Utils/ToolBox.cs @@ -224,6 +224,38 @@ namespace Barotrauma return inputType; } + /// + /// Convert a HSV value into a RGB value. + /// + /// Value between 0 and 360 + /// Value between 0 and 1 + /// Value between 0 and 1 + /// Reference + /// + public static Color HSVToRGB(float hue, float saturation, float value) + { + float c = value * saturation; + + float h = Math.Clamp(hue, 0, 360) / 60f; + + float x = c * (1 - Math.Abs(h % 2 - 1)); + + float r = 0, + g = 0, + b = 0; + + if (0 <= h && h <= 1) { r = c; g = x; b = 0; } + else if (1 < h && h <= 2) { r = x; g = c; b = 0; } + else if (2 < h && h <= 3) { r = 0; g = c; b = x; } + else if (3 < h && h <= 4) { r = 0; g = x; b = c; } + else if (4 < h && h <= 5) { r = x; g = 0; b = c; } + else if (5 < h && h <= 6) { r = c; g = 0; b = x; } + + float m = value - c; + + return new Color(r + m, g + m, b + m); + } + /// /// Returns either a green [x] or a red [o] /// @@ -491,6 +523,30 @@ namespace Barotrauma return destination; } + public static void SiftElement(this List list, int from, int to) + { + if (from < 0 || from >= list.Count) { throw new ArgumentException($"from parameter out of range (from={from}, range=[0..{list.Count - 1}])"); } + if (to < 0 || to >= list.Count) { throw new ArgumentException($"to parameter out of range (to={to}, range=[0..{list.Count - 1}])"); } + + T elem = list[from]; + if (from > to) + { + for (int i = from; i > to; i--) + { + list[i] = list[i - 1]; + } + list[to] = elem; + } + else if (from < to) + { + for (int i = from; i < to; i++) + { + list[i] = list[i + 1]; + } + list[to] = elem; + } + } + public static string ByteArrayToString(byte[] ba) { StringBuilder hex = new StringBuilder(ba.Length * 2); diff --git a/Barotrauma/BarotraumaShared/Submarines/Azimuth.sub b/Barotrauma/BarotraumaShared/Submarines/Azimuth.sub index 14074f2dd..38d4e7f67 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Azimuth.sub and b/Barotrauma/BarotraumaShared/Submarines/Azimuth.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index c12a1e85c..8c1105f88 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub index 0b90165e1..dac49f4f7 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub and b/Barotrauma/BarotraumaShared/Submarines/Kastrull.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index da40ce2e5..6a61c4af4 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index f384a238b..2b5732065 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub index aecb572ff..4518bcd0f 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon2.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Venture.sub b/Barotrauma/BarotraumaShared/Submarines/Venture.sub index 160f47b2f..0ce3c7daa 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Venture.sub and b/Barotrauma/BarotraumaShared/Submarines/Venture.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index af4053a15..1ffff4b95 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,5 +1,86 @@ --------------------------------------------------------------------------------------------------------- -v0.12.X.X +v0.1300.0.1 (unstable) +--------------------------------------------------------------------------------------------------------- + +Changes: +- 3 new outpost mission types: clearing a nest, hostage rescue and assassination. +- The enemies in abandoned outposts are tougher and there's more of them in multiplayer. +- Improvements to abandoned outpost modules. +- Respawning is disabled in abandoned outposts. +- Gating progress between biomes: you need a certain amount of money or reputation before you can enter the next biome. +- Reworked Charybdis as another abyss monster (in addition to Endworm). +- Revisited husk animations, movement speeds, and general balance. Husks regenerate a lot and are a bit tougher than they use to be. Human husks can and will easily rise again, unless properly killed. +- Adjustments and fixes on the thresher attacks. +- Revisit mudraptors' attacks. Fixes unarmored mudraptors and mudraptor hatchlings acting weird when attacking characters in water. +- Lower the overall damage of all the hatchlings. +- Bots can now be renamed through the outpost crew management interface. +- Fabricators now display remaining time when fabricating. +- Endworm now groans when the tail is cut. +- Beacon stations' reactors don't deteriorate or consuming fuel to prevent the station from going back down when the player is travelling out from the level. +- Irradiated outposts become abandoned instead of simply disappearing. +- Fabricator displays the time until ready when fabrication is in progress. +- The output length of all signal components is now restricted to 200 characters by default to prevent performance and networking issues if the output is set to an excessively long value (such as the entire dialog of the Bee Movie). The limit can be increased in the sub editor. +- Stunning still works on friendly characters even if friendly fire has been disabled on a server. +- Made radiation sickness's icon appear before it starts causing burns (instead of working the other way around). +- Made radiation sickness cause nausea. +- Added a checkbox to color component that toggles between RGBA and HSV input. +- Disabled NPC conversations in sub editor test mode. +- Added a tooltip when hovering over radiated area. +- Added a checkbox for the radiation feature to both singleplayer and multiplayer. +- Radiated outposts now turn into abandoned outposts instead of vanishing in thin air. +- Use ternary states for some of the server filters (thanks someone972!) +- Banning a player who's using Steam Family Sharing will now also ban the owner's account, solving ban evasion using this method. +- Added interactive submarine previews to the lobby and "New Game" screen. +- Implemented rudimentary text highlighting in mission descriptions. +- Added in-game hints, designed to help with new player onboarding. Can be disabled in the settings. + +Fixes: +- Attempt to fix crashes with the error message "failed to generate OpenAL/stream buffer" on Mac. +- Waypoint fixes in the abandoned outposts. +- Fixed bot reaction times and readjusted aiming delays. +- Fixed bots that follow you not always holding position in the combat mode. +- Fixed bots not being able to hit targets that lie on the ground. +- Fixed monsters sometimes targeting ruin rooms. +- Monsters now stop targeting characters that are far enough behind level geometry. Fixes them getting stuck while trying to reach targets that are not easily reachable. +- Fixes to the Endworm's behavior and ragdoll. +- Minor boost to the Endworm's attack on walls, major boost to the attack on characters. +- Fixed a crash when no valid limb was found for an affliction. Probably only happened with characters that already had some limbs severed. +- Fixed non-multiplied damage to limbs effectively always being clamped to 100, which means that no attack could do more than 100 damage per hit unless there's some damage modifier defined on the target limb. +- Fixed husk's mouth tentacles rendering on top of the left hand. Also fixed a minor texture bleeding on the waist. +- Fixed possible desync when multiple players use the crew management interface simultaneously. +- Fixed being able to drag order icons when spectating. +- Fixed non-interactable items being counted as owned in the store interface. +- Fixed abyss monsters not keeping in the depths and non-abyss monsters not avoiding the depths when they have attack targets. Now they should only attack the targets that come to their zone and continue pursuing the target until they either lose it or if the state changes from attack to something else. +- Fixed bots treating radiation sickness too eagerly (leading to wasted antirad). +- Added missing platforms to abandoned outpost modules. +- Fixed vanilla wrecks/modules/outposts being shown in the Workshop menu's publish tab. +- Fixed bioluminescent cave's hallucination effect never going fully away and made it treatable with haloperidol. +- Fixed characters getting impact damage from collisions with sensors. Caused characters to get stunned when they're thrown around by a monster while touching a level trigger (for instance, the branches in forest caves). +- Fixed held items not appearing in the characters hand after entering a new level in the campaign. +- Fixed hidden items being shown when searching for them in sub editor. +- Fixed crashing when trying to load a linked sub that contains no hulls. +- Fixed "hide incompatible" checkbox in the server list. +- Fixed one of the engineer variants having too many items in the toolbelt. +- Monster events don't spawn an additional endworm in the abyss if one has already been spawned by a hunting grounds mission. +- Fixed nav terminal ignoring velocity_in signals when the terminal hasn't been operated by anyone during the round. +- Fixed launching depth charges crashing the game. +- Fixed coilgun lights flickering when firing. +- Fixed ability to walk through the heavy doors in mining outposts. +- Fixed inability to move stacks of items to containers by double-clicking. +- Fixed crashing when trying to enter an abandoned outpost with a sub that has shuttles. +- Fixed monsters targeting entities outside of their allowed zone. They dropped the target when they reached the non-allowed zone, which caused indetermined behavior (fluctuating) at the border. +- Fixed monsters with the circling behavior sometimes not being able to reach the target because they were not targeting the closest target. +- Fixed Workshop mod download prompt looping and freezing when there's a hash mismatch after a mod has already been installed. + +Modding: +- Added AITrigger that can be used to trigger an ai state using the status effects (See Charybdis). +- Turned IsTraitor into a property so it can be accessed by status effects. + +Bots: +- Fixed non-security bots fleeing the enemy (in defensive combat state) even when they are ordered to fight intruders. + +--------------------------------------------------------------------------------------------------------- +v0.1300.0.0 (unstable) --------------------------------------------------------------------------------------------------------- Campaign changes: @@ -12,17 +93,21 @@ Campaign changes: Abyss: - Reintroduced Endworms. - Added floating islands that contain caves and rare minerals to the Abyss. -- Multiple current orders: you can assign up to three simultaneous orders for characters and drag the icons to change their priority. Changes: - Added abandoned outposts and a new abandoned outpost mission type. - Added entity subcategories to the submarine editor (note that most of the vanilla items/structures aren't categorized yet in this build). +- Reworked the impacts to the sub when it's hit by monsters. Increased the screen shake and stunning. There's now a 30 second cooldown for getting knocked down. Fixed the impact not triggering if the colliding limb is not big enough. - Reworked attacks and effects for the following creatures: Moloch, Black Moloch, Hammerhead, Hammerhead Matriarch, and Golden Hammerhead. They now have bigger impact on the sub when they hit it. Black Moloch's emp damage is halved. - Moloch's shell now always breaks when shot with a railgun. - Recreated waypoints for the vanilla subs. -- Added EMP effect to nuclear shells. +- Added EMP effect to nuclear shells. Increased the damage a bit. - Added a right click context menu option to copy debug console errors to clipboard. -- Railgun shells now explode instead of piercing armor. Physicorium shells still go through armor. +- Railgun shells now explode instead of piercing armor. Physicorium shells still go through armor. Make all railgun ammunition slightly more likely to break limb joints. +- Added multiple current orders: you can assign up to three simultaneous orders for characters and drag the icons to change their priority. +- NPCs (non-bots) speak report related lines less than they used to. +- The bleeding particles are now emitted from the last matching limb instead of the first. Readjusted particle emit frequency and scale. +- Reduced Moloch's bleeding reductions. Fixes: - Major improvements to the voice chat: higher audio quality, less intrusive radio effect, fixed clicks/distortion. @@ -43,6 +128,10 @@ Fixes: - Fixed status monitor in a docked shuttle sometimes not displaying the main sub. - Fixed campaign stores sometimes displaying prices for a previous location. - Fixed various scaling issues on higher resolutions. +- Fixed monsters fluttering while pursuing other creatures. Only happened while swimming and was not notable on all monsters). +- Fixed mouse cursor being switched to hand even when the spritesheet is not shown in the character editor. +- Fixed monsters with low head/torso torques moving backwards when they try to turn around 180 degrees. +- Fixed regular Moloch not bleeding correctly. Bots: - Bots now warn when you are running low / out of oxygen or welding fuel tanks, turret ammunition, or reactor fuel. @@ -58,11 +147,33 @@ Bots: - Fixed bots trying to return the diving suit when it's not a reasonable thing to do, but when they actually don't need it. - Fixed bots failing to swap an oxygen tank from a diving mask to a diving suit when both items are equipped. - Fixed bots getting confused when they have a mask without a valid oxygen tank inside it and when there's not enough oxygen in the room to be without the mask. +- Bots now react faster in general to all enemies. +- Bots now automatically attack enemies outside of the main sub, if they have a weapon. While docked to an enemy outpost or submarine, the "fight intruders" order now acts as an offensive order to attack the enemies on the connected submarine/outpost. +- Fixed bots following the player when the player is controlling a monster. +- You can now escape from NPCs by going far enough from them while they are pursuing you. +- Fixed bots reacting to attackers that are outside of the submarine. +- Fixed the "reportrange" parameter not working when the character is attacked. In practice only has effect on NPCs. +- Fixed report icons being shown also for other teams instead of just the own team. +- Bots now target the closest limb instead of the main collider when they aim with the turret. With small creatures, this should now make any difference. With long creatures, it allows the bots to target the extremities of the body instead of always shooting at the main body. Modding: +- The hit impact of a monster's attack can now be adjusted with the new "submarineimpactmultiplier" defined in the attack block. Note that this is a multipler to the actual impact, hence also the force applied on the attacking monster affects the final impact. - Explosions now have three new parameters: ignorecover, onlyinside, and onlyoutside. - Fixed crashing when attempting to play a music clip that isn't a valid ogg file or if the file is not found. - Fixed crashing when trying to spawn a character variant with custom inventory contents. +- Renamed the ai parameter "Threshold" as "DamageThreshold". +- Replaced "spawndeep" with "abyss" spawn type (defined in random events). +- New parameters StayInAbyss and StayInsideLevel that define the area where the creature tries to keep inside while not attacking. +- The creature disable distance, which totally disables the creature, is now exposed in the character parameters. The distance for triggering simple physics, which disables all the limbs and keeps only the main collider updated, is half of this distance. Increased the default from 22 000 to 25 000 (pixels). +- New attack pattern: Circle (around the target). Used by the abyss creatures. +- The aim speed and accuracy of NPC characters can now be adjusted in the npc (spawn) definition. The Aim speed also affects melee attack speed. +- Fixed OnSevered status effects launching also on the limb that the severed limb was attached to. +- Added "bleedingnonstop" affliction, which is just the same as normal bleeding but it never wears off. +- Added and option to target the last matching limb insted of the first (StatusEffect.TargetType.Limb). Implemented targeting other limbs even when the status effect is triggered from the limb, which was previously only implemented for status effects that targeted character. See Endworm for an example. +- Fixed hidden limbs not being ignored in many cases where they should, which potentially could cause issues with some custom monsters. +- Added "bleedparticlemultiplier" parameter in character definition, which can be used to increase/decrease the general amount of bleeding for the character in question. +- Added an option to always ignore an ai target if it's not inside the same sub as the character. +- Attacks can now "blink" limbs when they attack (Endworm). Blinking is a generic way to rotate limbs so that they "animate" (see Watcher's eye). --------------------------------------------------------------------------------------------------------- v0.12.0.3 diff --git a/Deploy/Linux/DeployLinux.sh b/Deploy/Linux/DeployLinux.sh old mode 100644 new mode 100755 diff --git a/Deploy/Linux/DeployLinuxUnstable.sh b/Deploy/Linux/DeployLinuxUnstable.sh old mode 100644 new mode 100755 diff --git a/HelperScripts/cleanup_obj.sh b/HelperScripts/cleanup_obj.sh old mode 100644 new mode 100755 diff --git a/Libraries/Concentus/CSharp/Concentus/Concentus.NetStandard.csproj b/Libraries/Concentus/CSharp/Concentus/Concentus.NetStandard.csproj index 99073a079..725dda808 100644 --- a/Libraries/Concentus/CSharp/Concentus/Concentus.NetStandard.csproj +++ b/Libraries/Concentus/CSharp/Concentus/Concentus.NetStandard.csproj @@ -1,20 +1,20 @@ - - - - netstandard2.1 - AnyCPU;x64 - Concentus - Logan Stromberg - 1.1.6.0 - Copyright © Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp. - This package is a pure portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams - - https://github.com/lostromb/concentus - - - - full - true - - - + + + + netstandard2.1 + AnyCPU;x64 + Concentus + Logan Stromberg + 1.1.6.0 + Copyright © Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp. + This package is a pure portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams + + https://github.com/lostromb/concentus + + + + full + true + + + diff --git a/Libraries/Farseer Physics Engine 3.5/Farseer.NetStandard.csproj b/Libraries/Farseer Physics Engine 3.5/Farseer.NetStandard.csproj index 69fbafa2f..96e8ca92c 100644 --- a/Libraries/Farseer Physics Engine 3.5/Farseer.NetStandard.csproj +++ b/Libraries/Farseer Physics Engine 3.5/Farseer.NetStandard.csproj @@ -1,40 +1,40 @@ - - - - netstandard2.1 - FarseerPhysics - Copyright Ian Qvist © 2013 - Farseer Physics Engine - - 3.5.0.0 - Ian Qvist - AnyCPU;x64 - - - - TRACE - portable - true - - - - - - - - - - - - - - - - - - - - - - - + + + + netstandard2.1 + FarseerPhysics + Copyright Ian Qvist © 2013 + Farseer Physics Engine + + 3.5.0.0 + Ian Qvist + AnyCPU;x64 + + + + TRACE + portable + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj b/Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj index cd6803b0e..aab701817 100644 --- a/Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj +++ b/Libraries/GameAnalytics/GA_SDK_NETSTANDARD/GA_SDK_NETSTANDARD.csproj @@ -1,35 +1,35 @@ - - - - netstandard2.1 - GameAnalytics.NetStandard - GameAnalytics.Net - AnyCPU;x64 - Game Analytics - Copyright (c) 2016 Game Analytics - - - - TRACE;MONO - - - - TRACE;MONO - - - - TRACE;MONO - - - - TRACE;MONO - - - - - - - - - - + + + + netstandard2.1 + GameAnalytics.NetStandard + GameAnalytics.Net + AnyCPU;x64 + Game Analytics + Copyright (c) 2016 Game Analytics + + + + TRACE;MONO + + + + TRACE;MONO + + + + TRACE;MONO + + + + TRACE;MONO + + + + + + + + + + diff --git a/Libraries/Hyper.ComponentModel/Hyper.ComponentModel.NetStandard.csproj b/Libraries/Hyper.ComponentModel/Hyper.ComponentModel.NetStandard.csproj index 4cd0d3594..3e53cc156 100644 --- a/Libraries/Hyper.ComponentModel/Hyper.ComponentModel.NetStandard.csproj +++ b/Libraries/Hyper.ComponentModel/Hyper.ComponentModel.NetStandard.csproj @@ -1,16 +1,16 @@ - - - - netstandard2.1 - Pavel Nosovich - - Hyper.ComponentModel - Copyright (c) 2014 Pavel Nosovich - AnyCPU;x64 - - - - - - - + + + + netstandard2.1 + Pavel Nosovich + + Hyper.ComponentModel + Copyright (c) 2014 Pavel Nosovich + AnyCPU;x64 + + + + + + + diff --git a/Libraries/Lidgren.Network/Lidgren.NetStandard.csproj b/Libraries/Lidgren.Network/Lidgren.NetStandard.csproj index 3e265503b..4cf45b8e4 100644 --- a/Libraries/Lidgren.Network/Lidgren.NetStandard.csproj +++ b/Libraries/Lidgren.Network/Lidgren.NetStandard.csproj @@ -1,33 +1,33 @@ - - - - netstandard2.1 - Lidgren - - Lidgren.Network - Copyright (c) 2015 lidgren - 2012.1.7.0 - AnyCPU;x64 - - - - 1701;1702;3021 - - - - 1701;1702;3021 - - - - 1701;1702;3021 - - - - 1701;1702;3021 - - - - - - - + + + + netstandard2.1 + Lidgren + + Lidgren.Network + Copyright (c) 2015 lidgren + 2012.1.7.0 + AnyCPU;x64 + + + + 1701;1702;3021 + + + + 1701;1702;3021 + + + + 1701;1702;3021 + + + + 1701;1702;3021 + + + + + + + diff --git a/Libraries/MonoGame.Framework/Src/.gitignore b/Libraries/MonoGame.Framework/Src/.gitignore index f605f607e..b0bd399e1 100644 --- a/Libraries/MonoGame.Framework/Src/.gitignore +++ b/Libraries/MonoGame.Framework/Src/.gitignore @@ -1,93 +1,93 @@ -#OS junk files -[Tt]humbs.db -*.DS_Store - -#Output Linux Installer -Installers/Linux/tmp_deb/ -Installers/Linux/tmp_run/ -*.run -*.deb - -#Visual Studio files -*.pidb -*.userprefs -*.[Oo]bj -*.exe -*.pdb -*.user -*.aps -*.pch -*.vspscc -*.vssscc -*_i.c -*_p.c -*.ncb -*.suo -*.tlb -*.tlh -*.bak -*.[Cc]ache -*.ilk -*.log -*.lib -*.sbr -*.sdf -*.csproj.csdat -ipch/ -obj/ -[Bb]in -[Dd]ebug*/ -[Rr]elease*/ -Ankh.NoLoad -.vs/ -project.lock.json -/MonoGame.Framework/MonoGame.Framework.Net.WindowsUniversal.project.lock.json -/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.project.lock.json -artifacts/ - -# JetBrains Rider -.idea/ - -#Tooling -_ReSharper*/ -*.resharper -[Tt]est[Rr]esult* - -#Visual Studio Rebracer extension, allows the user to automatically change the style configuration by project -rebracer.xml - -#Subversion files -.svn - -# Office Temp Files -~$* - -#monodroid private beta -monodroid*.msi - -#Unix temporary files -*~ - -# Output docs -Documentation/Output/ - -# Protobuild Generated Files -*.speccache -*.ncrunchproject -*.ncrunchsolution - -#Mac Package Files -*.pkg -*.mpack -**/packages - -#Nuget Packages -**/*.nupkg - -#Zip files -*.zip - -Installers/MacOS/Scripts/Framework/postinstall -IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/MonoGame.Framework.dll.config - -ThirdParty/* +#OS junk files +[Tt]humbs.db +*.DS_Store + +#Output Linux Installer +Installers/Linux/tmp_deb/ +Installers/Linux/tmp_run/ +*.run +*.deb + +#Visual Studio files +*.pidb +*.userprefs +*.[Oo]bj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.csproj.csdat +ipch/ +obj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad +.vs/ +project.lock.json +/MonoGame.Framework/MonoGame.Framework.Net.WindowsUniversal.project.lock.json +/MonoGame.Framework/MonoGame.Framework.WindowsUniversal.project.lock.json +artifacts/ + +# JetBrains Rider +.idea/ + +#Tooling +_ReSharper*/ +*.resharper +[Tt]est[Rr]esult* + +#Visual Studio Rebracer extension, allows the user to automatically change the style configuration by project +rebracer.xml + +#Subversion files +.svn + +# Office Temp Files +~$* + +#monodroid private beta +monodroid*.msi + +#Unix temporary files +*~ + +# Output docs +Documentation/Output/ + +# Protobuild Generated Files +*.speccache +*.ncrunchproject +*.ncrunchsolution + +#Mac Package Files +*.pkg +*.mpack +**/packages + +#Nuget Packages +**/*.nupkg + +#Zip files +*.zip + +Installers/MacOS/Scripts/Framework/postinstall +IDE/MonoDevelop/MonoDevelop.MonoGame/templates/Common/MonoGame.Framework.dll.config + +ThirdParty/* diff --git a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SpriteBatch.cs b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SpriteBatch.cs index 4b12f5c1f..a2cc3d4e3 100644 --- a/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SpriteBatch.cs +++ b/Libraries/MonoGame.Framework/Src/MonoGame.Framework/Graphics/SpriteBatch.cs @@ -7,10 +7,23 @@ using System.Text; namespace Microsoft.Xna.Framework.Graphics { + public interface ISpriteBatch + { + public void Draw(Texture2D texture, + Vector2 position, + Rectangle? sourceRectangle, + Color color, + float rotation, + Vector2 origin, + Vector2 scale, + SpriteEffects effects, + float layerDepth); + } + /// /// Helper class for drawing text strings and sprites in one or more optimized batches. /// - public class SpriteBatch : GraphicsResource + public class SpriteBatch : GraphicsResource, ISpriteBatch { #region Private Fields readonly SpriteBatcher _batcher; diff --git a/Libraries/SharpFont/Source/SharpFont/SharpFont.NetStandard.csproj b/Libraries/SharpFont/Source/SharpFont/SharpFont.NetStandard.csproj index 6f2368424..a4477adaa 100644 --- a/Libraries/SharpFont/Source/SharpFont/SharpFont.NetStandard.csproj +++ b/Libraries/SharpFont/Source/SharpFont/SharpFont.NetStandard.csproj @@ -1,45 +1,45 @@ - - - - netstandard2.1 - SharpFont - SharpFont - Cross-platform FreeType bindings for C# - Robmaister - SharpFont - - Copyright (c) Robert Rouhani 2012-2016 - AnyCPU;x64 - - - - TRACE;DEBUG;SHARPFONT_PORTABLE - true - - - - TRACE;DEBUG;SHARPFONT_PORTABLE - true - 1701;1702;3021 - - - - TRACE;SHARPFONT_PORTABLE - true - - - - TRACE;SHARPFONT_PORTABLE - true - 1701;1702;3021 - - - - - - - - - - - + + + + netstandard2.1 + SharpFont + SharpFont + Cross-platform FreeType bindings for C# + Robmaister + SharpFont + + Copyright (c) Robert Rouhani 2012-2016 + AnyCPU;x64 + + + + TRACE;DEBUG;SHARPFONT_PORTABLE + true + + + + TRACE;DEBUG;SHARPFONT_PORTABLE + true + 1701;1702;3021 + + + + TRACE;SHARPFONT_PORTABLE + true + + + + TRACE;SHARPFONT_PORTABLE + true + 1701;1702;3021 + + + + + + + + + + + diff --git a/Libraries/XNATypes/XNATypes.csproj b/Libraries/XNATypes/XNATypes.csproj index b3b90d794..57fd8b083 100644 --- a/Libraries/XNATypes/XNATypes.csproj +++ b/Libraries/XNATypes/XNATypes.csproj @@ -1,10 +1,10 @@ - - - - netstandard2.1 - AnyCPU;x64 - - - - - + + + + netstandard2.1 + AnyCPU;x64 + + + + + diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/common.props b/Libraries/webm_mem_playback/opus/win32/VS2015/common.props index 6c757d8b7..03cd45b0c 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/common.props +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/common.props @@ -1,82 +1,82 @@ - - - - - - $(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\$(ProjectName)\ - Unicode - - - true - true - false - - - false - false - true - - - - Level3 - false - false - ..\..;..\..\include;..\..\silk;..\..\celt;..\..\win32;%(AdditionalIncludeDirectories) - HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - false - false - - - Console - - - true - Console - - - - - Guard - ProgramDatabase - NoExtensions - false - true - false - Disabled - false - false - Disabled - MultiThreadedDebug - MultiThreadedDebugDLL - true - false - - - true - - - - - false - None - true - true - false - Speed - Fast - Precise - true - true - true - MaxSpeed - MultiThreaded - MultiThreadedDLL - 16Bytes - - - false - - - + + + + + + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + Unicode + + + true + true + false + + + false + false + true + + + + Level3 + false + false + ..\..;..\..\include;..\..\silk;..\..\celt;..\..\win32;%(AdditionalIncludeDirectories) + HAVE_CONFIG_H;WIN32;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + false + + + Console + + + true + Console + + + + + Guard + ProgramDatabase + NoExtensions + false + true + false + Disabled + false + false + Disabled + MultiThreadedDebug + MultiThreadedDebugDLL + true + false + + + true + + + + + false + None + true + true + false + Speed + Fast + Precise + true + true + true + MaxSpeed + MultiThreaded + MultiThreadedDLL + 16Bytes + + + false + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj b/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj index ae420d508..fc2241116 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj @@ -1,399 +1,399 @@ - - - - - DebugDLL_fixed - Win32 - - - DebugDLL_fixed - x64 - - - DebugDLL - Win32 - - - DebugDLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - ReleaseDLL_fixed - Win32 - - - ReleaseDLL_fixed - x64 - - - ReleaseDLL - Win32 - - - ReleaseDLL - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - opus - {219EC965-228A-1824-174D-96449D05F88A} - - - - StaticLibrary - v142 - - - DynamicLibrary - v142 - - - DynamicLibrary - v142 - - - StaticLibrary - v142 - - - DynamicLibrary - v142 - - - DynamicLibrary - v142 - - - StaticLibrary - v142 - - - DynamicLibrary - v142 - - - DynamicLibrary - v142 - - - StaticLibrary - v142 - - - DynamicLibrary - v142 - - - DynamicLibrary - v142 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\silk\fixed;..\..\silk\float;%(AdditionalIncludeDirectories) - DLL_EXPORT;%(PreprocessorDefinitions) - FIXED_POINT;%(PreprocessorDefinitions) - /arch:IA32 %(AdditionalOptions) - - - /ignore:4221 %(AdditionalOptions) - - - "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION - Generating version.h - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 4244;%(DisableSpecificWarnings) - - - - - - - - - - - - - - - false - - - false - - - true - - - - - - - true - - - true - - - false - - - - - - - + + + + + DebugDLL_fixed + Win32 + + + DebugDLL_fixed + x64 + + + DebugDLL + Win32 + + + DebugDLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + ReleaseDLL_fixed + Win32 + + + ReleaseDLL_fixed + x64 + + + ReleaseDLL + Win32 + + + ReleaseDLL + x64 + + + Release + Win32 + + + Release + x64 + + + + Win32Proj + opus + {219EC965-228A-1824-174D-96449D05F88A} + + + + StaticLibrary + v142 + + + DynamicLibrary + v142 + + + DynamicLibrary + v142 + + + StaticLibrary + v142 + + + DynamicLibrary + v142 + + + DynamicLibrary + v142 + + + StaticLibrary + v142 + + + DynamicLibrary + v142 + + + DynamicLibrary + v142 + + + StaticLibrary + v142 + + + DynamicLibrary + v142 + + + DynamicLibrary + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\silk\fixed;..\..\silk\float;%(AdditionalIncludeDirectories) + DLL_EXPORT;%(PreprocessorDefinitions) + FIXED_POINT;%(PreprocessorDefinitions) + /arch:IA32 %(AdditionalOptions) + + + /ignore:4221 %(AdditionalOptions) + + + "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION + Generating version.h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4244;%(DisableSpecificWarnings) + + + + + + + + + + + + + + + false + + + false + + + true + + + + + + + true + + + true + + + false + + + + + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj.filters b/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj.filters index 97eb46551..47185c67d 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj.filters +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/opus.vcxproj.filters @@ -1,744 +1,744 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj b/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj index 7ad4b5e21..fcd971bb6 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj @@ -1,171 +1,171 @@ - - - - - DebugDLL_fixed - Win32 - - - DebugDLL_fixed - x64 - - - DebugDLL - Win32 - - - DebugDLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - ReleaseDLL_fixed - Win32 - - - ReleaseDLL_fixed - x64 - - - ReleaseDLL - Win32 - - - ReleaseDLL - x64 - - - Release - Win32 - - - Release - x64 - - - - - {219ec965-228a-1824-174d-96449d05f88a} - - - - - - - {016C739D-6389-43BF-8D88-24B2BF6F620F} - Win32Proj - opus_demo - - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + DebugDLL_fixed + Win32 + + + DebugDLL_fixed + x64 + + + DebugDLL + Win32 + + + DebugDLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + ReleaseDLL_fixed + Win32 + + + ReleaseDLL_fixed + x64 + + + ReleaseDLL + Win32 + + + ReleaseDLL + x64 + + + Release + Win32 + + + Release + x64 + + + + + {219ec965-228a-1824-174d-96449d05f88a} + + + + + + + {016C739D-6389-43BF-8D88-24B2BF6F620F} + Win32Proj + opus_demo + + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj.filters b/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj.filters index 2eb113ac8..dbcc8ae92 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj.filters +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/opus_demo.vcxproj.filters @@ -1,22 +1,22 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj index 4ba7c8ae5..e428bd3f7 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj @@ -1,171 +1,171 @@ - - - - - DebugDLL_fixed - Win32 - - - DebugDLL_fixed - x64 - - - DebugDLL - Win32 - - - DebugDLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - ReleaseDLL_fixed - Win32 - - - ReleaseDLL_fixed - x64 - - - ReleaseDLL - Win32 - - - ReleaseDLL - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - {219ec965-228a-1824-174d-96449d05f88a} - - - - {1D257A17-D254-42E5-82D6-1C87A6EC775A} - Win32Proj - test_opus_api - - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + DebugDLL_fixed + Win32 + + + DebugDLL_fixed + x64 + + + DebugDLL + Win32 + + + DebugDLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + ReleaseDLL_fixed + Win32 + + + ReleaseDLL_fixed + x64 + + + ReleaseDLL + Win32 + + + ReleaseDLL + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + {219ec965-228a-1824-174d-96449d05f88a} + + + + {1D257A17-D254-42E5-82D6-1C87A6EC775A} + Win32Proj + test_opus_api + + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj.filters b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj.filters index 383d19f71..070c8ab01 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj.filters +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_api.vcxproj.filters @@ -1,14 +1,14 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj index 8e4640094..cbf562183 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj @@ -1,171 +1,171 @@ - - - - - DebugDLL_fixed - Win32 - - - DebugDLL_fixed - x64 - - - DebugDLL - Win32 - - - DebugDLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - ReleaseDLL_fixed - Win32 - - - ReleaseDLL_fixed - x64 - - - ReleaseDLL - Win32 - - - ReleaseDLL - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - {219ec965-228a-1824-174d-96449d05f88a} - - - - {8578322A-1883-486B-B6FA-E0094B65C9F2} - Win32Proj - test_opus_api - - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + DebugDLL_fixed + Win32 + + + DebugDLL_fixed + x64 + + + DebugDLL + Win32 + + + DebugDLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + ReleaseDLL_fixed + Win32 + + + ReleaseDLL_fixed + x64 + + + ReleaseDLL + Win32 + + + ReleaseDLL + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + {219ec965-228a-1824-174d-96449d05f88a} + + + + {8578322A-1883-486B-B6FA-E0094B65C9F2} + Win32Proj + test_opus_api + + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj.filters b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj.filters index 3036a4e70..588637e83 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj.filters +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_decode.vcxproj.filters @@ -1,14 +1,14 @@ - - - - - {4a0dd677-931f-4728-afe5-b761149fc7eb} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - - - Source Files - - + + + + + {4a0dd677-931f-4728-afe5-b761149fc7eb} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj index 6804918a3..5a313c31d 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj @@ -1,172 +1,172 @@ - - - - - DebugDLL_fixed - Win32 - - - DebugDLL_fixed - x64 - - - DebugDLL - Win32 - - - DebugDLL - x64 - - - Debug - Win32 - - - Debug - x64 - - - ReleaseDLL_fixed - Win32 - - - ReleaseDLL_fixed - x64 - - - ReleaseDLL - Win32 - - - ReleaseDLL - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - - {219ec965-228a-1824-174d-96449d05f88a} - - - - {84DAA768-1A38-4312-BB61-4C78BB59E5B8} - Win32Proj - test_opus_api - - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - Application - v142 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + DebugDLL_fixed + Win32 + + + DebugDLL_fixed + x64 + + + DebugDLL + Win32 + + + DebugDLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + ReleaseDLL_fixed + Win32 + + + ReleaseDLL_fixed + x64 + + + ReleaseDLL + Win32 + + + ReleaseDLL + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + {219ec965-228a-1824-174d-96449d05f88a} + + + + {84DAA768-1A38-4312-BB61-4C78BB59E5B8} + Win32Proj + test_opus_api + + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + Application + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj.filters b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj.filters index 4ed3bb9e7..f04776380 100644 --- a/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj.filters +++ b/Libraries/webm_mem_playback/opus/win32/VS2015/test_opus_encode.vcxproj.filters @@ -1,17 +1,17 @@ - - - - - {546c8d9a-103e-4f78-972b-b44e8d3c8aba} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - - - Source Files - - - Source Files - - + + + + + {546c8d9a-103e-4f78-972b-b44e8d3c8aba} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/Libraries/webm_mem_playback/opus/win32/genversion.bat b/Libraries/webm_mem_playback/opus/win32/genversion.bat index 1def7460b..aea557393 100644 --- a/Libraries/webm_mem_playback/opus/win32/genversion.bat +++ b/Libraries/webm_mem_playback/opus/win32/genversion.bat @@ -1,37 +1,37 @@ -@echo off - -setlocal enableextensions enabledelayedexpansion - -for /f %%v in ('cd "%~dp0.." ^&^& git status ^>NUL 2^>NUL ^&^& git describe --tags --match "v*" --dirty 2^>NUL') do set version=%%v - -if not "%version%"=="" set version=!version:~1! && goto :gotversion - -if exist "%~dp0..\package_version" goto :getversion - -echo Git cannot be found, nor can package_version. Generating unknown version. - -set version=unknown - -goto :gotversion - -:getversion - -for /f "delims== tokens=2" %%v in (%~dps0..\package_version) do set version=%%v -set version=!version:"=! - -:gotversion - -set version=!version: =! -set version_out=#define %~2 "%version%" - -echo %version_out%> "%~1_temp" - -echo n | comp "%~1_temp" "%~1" > NUL 2> NUL - -if not errorlevel 1 goto exit - -copy /y "%~1_temp" "%~1" - -:exit - -del "%~1_temp" +@echo off + +setlocal enableextensions enabledelayedexpansion + +for /f %%v in ('cd "%~dp0.." ^&^& git status ^>NUL 2^>NUL ^&^& git describe --tags --match "v*" --dirty 2^>NUL') do set version=%%v + +if not "%version%"=="" set version=!version:~1! && goto :gotversion + +if exist "%~dp0..\package_version" goto :getversion + +echo Git cannot be found, nor can package_version. Generating unknown version. + +set version=unknown + +goto :gotversion + +:getversion + +for /f "delims== tokens=2" %%v in (%~dps0..\package_version) do set version=%%v +set version=!version:"=! + +:gotversion + +set version=!version: =! +set version_out=#define %~2 "%version%" + +echo %version_out%> "%~1_temp" + +echo n | comp "%~1_temp" "%~1" > NUL 2> NUL + +if not errorlevel 1 goto exit + +copy /y "%~1_temp" "%~1" + +:exit + +del "%~1_temp" diff --git a/Libraries/webm_mem_playback/webm_mem_playback/webm-mem-playback.vcxproj b/Libraries/webm_mem_playback/webm_mem_playback/webm-mem-playback.vcxproj index 6e90faa56..5a3253ee6 100644 --- a/Libraries/webm_mem_playback/webm_mem_playback/webm-mem-playback.vcxproj +++ b/Libraries/webm_mem_playback/webm_mem_playback/webm-mem-playback.vcxproj @@ -1,148 +1,148 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {D0097438-DA4F-4E6D-87AC-7D99DDD276B2} - vpxmemplayback - 10.0 - webm_mem_playback - - - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - v142 - true - MultiByte - - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - v142 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(ProjectName)_$(Platform) - - - - Level3 - Disabled - true - true - ..\libwebm_x86_64_vs15;..\libvpx_x86_64_vs15;%(AdditionalIncludeDirectories) - - - %(AdditionalDependencies) - - - - - Level3 - Disabled - true - true - %(AdditionalIncludeDirectories) - MultiThreadedDebug - - - %(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - ..\libwebm_x86_64_vs15;..\libvpx_x86_64_vs15;%(AdditionalIncludeDirectories) - - - true - true - %(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - ..\libwebm_x86_vs19;..\libvpx_x64_vs15;..\opus\include;%(AdditionalIncludeDirectories) - MultiThreaded - Speed - - - true - true - ../libvpx_x64_vs15/$(Platform)/$(Configuration)/vpxmt.lib;../libwebm_x64_vs19/Release/libwebm.lib;../opus/win32/VS2015/x64/Release/opus.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D0097438-DA4F-4E6D-87AC-7D99DDD276B2} + vpxmemplayback + 10.0 + webm_mem_playback + + + + DynamicLibrary + true + v142 + MultiByte + + + DynamicLibrary + false + v142 + true + MultiByte + + + DynamicLibrary + true + v142 + MultiByte + + + DynamicLibrary + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + $(ProjectName)_$(Platform) + + + + Level3 + Disabled + true + true + ..\libwebm_x86_64_vs15;..\libvpx_x86_64_vs15;%(AdditionalIncludeDirectories) + + + %(AdditionalDependencies) + + + + + Level3 + Disabled + true + true + %(AdditionalIncludeDirectories) + MultiThreadedDebug + + + %(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libwebm_x86_64_vs15;..\libvpx_x86_64_vs15;%(AdditionalIncludeDirectories) + + + true + true + %(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libwebm_x86_vs19;..\libvpx_x64_vs15;..\opus\include;%(AdditionalIncludeDirectories) + MultiThreaded + Speed + + + true + true + ../libvpx_x64_vs15/$(Platform)/$(Configuration)/vpxmt.lib;../libwebm_x64_vs19/Release/libwebm.lib;../opus/win32/VS2015/x64/Release/opus.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file