From 5dc31c213fceba2a6e993bccdf6af7933fe25084 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Mon, 18 Mar 2019 22:31:57 +0200 Subject: [PATCH] ad0bbaf...7245c72 commit 7245c721339885d062567befc052a592391b3b4a Author: Joonas Rikkonen Date: Sun Mar 10 15:22:31 2019 +0200 Fixed StatusEffects only applying afflictions to one limb even if the target is "Character" instead of "Limb", added a subtle screen distortion effect to heavy radiation sickness. Closes #1256 commit e0db27e62ec9546fd4b182a0cc97f7e5830645ae Author: Joonas Rikkonen Date: Sat Mar 9 21:53:51 2019 +0200 Fixed WrapText adding unnecessary spaces after every line break. Closes #1215 commit 988bc58d51c195ad9265b84a1e97e0101cd3f808 Author: Joonas Rikkonen Date: Sat Mar 9 21:12:50 2019 +0200 Fixed crashing when attempting to create a body for a wall section that's less than 1 unit long (e.g. if a wall that's just slightly longer than the wall section size receives damage). commit 8c31157425a9e2ec02312618d1bfa359ab3ee87d Author: Joonas Rikkonen Date: Sat Mar 9 20:30:44 2019 +0200 Fixed clients being unable to toggle the respawn shuttle on/off commit a4ccb039219830efe9cd305c26942dda1bd04e9c Author: Joonas Rikkonen Date: Sat Mar 9 19:33:22 2019 +0200 Fixed inability to select the respawn shuttle as a client host commit b89b2d2c282d8c74d7ccd37b3f29dcab51eff680 Author: Joonas Rikkonen Date: Sat Mar 9 19:32:41 2019 +0200 Made it possible to edit the style of the ListBox under GUIDropDowns, increased the opacity of the listbox to make the contents more readable when there's text behind it commit 8f6d9aef3d637fe37a18c78f4b15ef8fd266374e Author: Joonas Rikkonen Date: Sat Mar 9 18:11:23 2019 +0200 Fixed NetLobbyScreen not showing the names of the submarines the client doesn't have --- .../BarotraumaClient/Source/DebugConsole.cs | 549 ++++++++++++++++++ .../BarotraumaClient/Source/GUI/GUIButton.cs | 6 + .../Source/GUI/GUIDropDown.cs | 3 +- .../Source/GUI/GUITextBlock.cs | 26 +- .../Source/GameSession/CrewManager.cs | 101 +--- .../BarotraumaClient/Source/GameSettings.cs | 22 + .../Source/Items/Components/ItemComponent.cs | 57 ++ .../BarotraumaClient/Source/Items/Item.cs | 19 +- .../Source/Networking/GameClient.cs | 15 +- .../Source/Networking/ServerSettings.cs | 4 +- .../Source/Screens/CharacterEditorScreen.cs | 37 -- .../Source/Screens/MainMenuScreen.cs | 6 + .../Source/Screens/NetLobbyScreen.cs | 43 +- .../BarotraumaClient/Source/Utils/ToolBox.cs | 2 +- .../Source/Networking/GameServer.cs | 18 +- .../Source/Networking/ServerSettings.cs | 2 + .../Source/Screens/NetLobbyScreen.cs | 41 +- Barotrauma/BarotraumaShared/Icon.ico | Bin 16958 -> 102803 bytes .../AI/Objectives/AIObjectiveCombat.cs | 10 +- .../Source/Characters/AI/PathFinder.cs | 19 +- .../Animation/FishAnimController.cs | 12 +- .../Characters/Health/DamageModifier.cs | 3 +- .../BarotraumaShared/Source/DebugConsole.cs | 7 +- .../BarotraumaShared/Source/GameSettings.cs | 82 +++ .../Items/Components/Machines/Steering.cs | 2 +- .../BarotraumaShared/Source/Map/Map/Map.cs | 7 + .../BarotraumaShared/Source/Map/Structure.cs | 4 +- .../BarotraumaShared/Source/Map/Submarine.cs | 2 +- .../Source/Networking/ServerSettings.cs | 8 +- .../Source/Networking/Voting.cs | 5 +- .../BarotraumaShared/Source/PlayerInput.cs | 10 + .../Source/StatusEffects/StatusEffect.cs | 9 +- 32 files changed, 928 insertions(+), 203 deletions(-) diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index 0e7a33e97..1e124efe2 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -1965,6 +1965,555 @@ namespace Barotrauma TextManager.WriteToCSV(); NPCConversation.WriteToCSV(); })); +#endif + + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); + + commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) => + { + float defaultZoom = Screen.Selected.Cam.DefaultZoom; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out defaultZoom); + + float zoomSmoothness = Screen.Selected.Cam.ZoomSmoothness; + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out zoomSmoothness); + float moveSmoothness = Screen.Selected.Cam.MoveSmoothness; + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out moveSmoothness); + + float minZoom = Screen.Selected.Cam.MinZoom; + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out minZoom); + float maxZoom = Screen.Selected.Cam.MaxZoom; + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out maxZoom); + + Screen.Selected.Cam.DefaultZoom = defaultZoom; + Screen.Selected.Cam.ZoomSmoothness = zoomSmoothness; + Screen.Selected.Cam.MoveSmoothness = moveSmoothness; + Screen.Selected.Cam.MinZoom = minZoom; + Screen.Selected.Cam.MaxZoom = maxZoom; + })); + + commands.Add(new Command("waterparams", "waterparams [distortionscalex] [distortionscaley] [distortionstrengthx] [distortionstrengthy] [bluramount]: default 0.5 0.5 0.5 0.5 1", (string[] args) => + { + float distortScaleX = 0.5f, distortScaleY = 0.5f; + float distortStrengthX = 0.5f, distortStrengthY = 0.5f; + float blurAmount = 0.0f; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleX); + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleY); + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthX); + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthY); + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out blurAmount); + WaterRenderer.DistortionScale = new Vector2(distortScaleX, distortScaleY); + WaterRenderer.DistortionStrength = new Vector2(distortStrengthX, distortStrengthY); + WaterRenderer.BlurAmount = blurAmount; + })); + + + commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) => + { + //TODO: maybe do this automatically during loading when possible? + if (Screen.Selected == GameMain.SubEditorScreen) + { + if (!MapEntity.SelectedAny) + { + ThrowError("You have to select item(s) first!"); + } + else + { + foreach (var mapEntity in MapEntity.SelectedList) + { + if (mapEntity is Item item) + { + item.Rect = new Rectangle(item.Rect.X, item.Rect.Y, + (int)(item.Prefab.sprite.size.X * item.Prefab.Scale), + (int)(item.Prefab.sprite.size.Y * item.Prefab.Scale)); + } + else if (mapEntity is Structure structure) + { + if (!structure.ResizeHorizontal) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + (int)structure.Prefab.ScaledSize.X, + structure.Rect.Height); + } + if (!structure.ResizeVertical) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + structure.Rect.Width, + (int)structure.Prefab.ScaledSize.Y); + } + } + } + } + } + }, isCheat: false)); +#endif + + GameMain.Config.SaveNewPlayerConfig(); + + commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => + { + string sourcePath = args.Length > 0 ? args[0] : "Content/Texts/EnglishVanilla.txt"; + string destinationPath = args.Length > 1 ? args[1] : "Content/Texts/EnglishVanilla.xml"; + + string[] lines; + try + { + lines = File.ReadAllLines(sourcePath); + } + catch (Exception e) + { + ThrowError("Reading the file \"" + sourcePath + "\" failed.", e); + return; + } + var doc = XMLExtensions.TryLoadXml(destinationPath); + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (i >= lines.Length) + { + ThrowError("Error while loading texts to the xml file. The xml has more elements than the number of lines in the text file."); + return; + } + element.Value = lines[i]; + i++; + } + doc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Select(f => f.Replace("\\", "/")); + return new string[][] + { + files.Where(f => Path.GetExtension(f)==".txt").ToArray(), + files.Where(f => Path.GetExtension(f)==".xml").ToArray() + }; + })); + + commands.Add(new Command("updatetextfile", "updatetextfile [sourcefile] [destinationfile]: Inserts all the xml elements that are only present in the source file into the destination file. Can be used to update outdated translation files more easily.", (string[] args) => + { + if (args.Length < 2) return; + string sourcePath = args[0]; + string destinationPath = args[1]; + + var sourceDoc = XMLExtensions.TryLoadXml(sourcePath); + var destinationDoc = XMLExtensions.TryLoadXml(destinationPath); + + XElement destinationElement = destinationDoc.Root.Elements().First(); + foreach (XElement element in sourceDoc.Root.Elements()) + { + if (destinationDoc.Root.Element(element.Name) == null) + { + element.Value = "!!!!!!!!!!!!!" + element.Value; + destinationElement.AddAfterSelf(element); + } + XNode nextNode = destinationElement.NextNode; + while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; + destinationElement = nextNode as XElement; + } + destinationDoc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Where(f => Path.GetExtension(f) == ".xml").Select(f => f.Replace("\\", "/")).ToArray(); + return new string[][] + { + files, + files + }; + })); + + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => + { + string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; + List lines = new List(); + foreach (MapEntityPrefab me in MapEntityPrefab.List) + { + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); + } + File.WriteAllLines(filePath, lines); + })); +#if DEBUG + commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => + { + if (args.Length != 1) return; + TextManager.CheckForDuplicates(args[0]); + })); + + commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) => + { + TextManager.WriteToCSV(); + NPCConversation.WriteToCSV(); + })); +#endif + + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); + + commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) => + { + float defaultZoom = Screen.Selected.Cam.DefaultZoom; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out defaultZoom); + + float zoomSmoothness = Screen.Selected.Cam.ZoomSmoothness; + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out zoomSmoothness); + float moveSmoothness = Screen.Selected.Cam.MoveSmoothness; + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out moveSmoothness); + + float minZoom = Screen.Selected.Cam.MinZoom; + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out minZoom); + float maxZoom = Screen.Selected.Cam.MaxZoom; + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out maxZoom); + + Screen.Selected.Cam.DefaultZoom = defaultZoom; + Screen.Selected.Cam.ZoomSmoothness = zoomSmoothness; + Screen.Selected.Cam.MoveSmoothness = moveSmoothness; + Screen.Selected.Cam.MinZoom = minZoom; + Screen.Selected.Cam.MaxZoom = maxZoom; + })); + + commands.Add(new Command("waterparams", "waterparams [distortionscalex] [distortionscaley] [distortionstrengthx] [distortionstrengthy] [bluramount]: default 0.5 0.5 0.5 0.5 1", (string[] args) => + { + float distortScaleX = 0.5f, distortScaleY = 0.5f; + float distortStrengthX = 0.5f, distortStrengthY = 0.5f; + float blurAmount = 0.0f; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleX); + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleY); + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthX); + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthY); + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out blurAmount); + WaterRenderer.DistortionScale = new Vector2(distortScaleX, distortScaleY); + WaterRenderer.DistortionStrength = new Vector2(distortStrengthX, distortStrengthY); + WaterRenderer.BlurAmount = blurAmount; + })); + + + commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) => + { + //TODO: maybe do this automatically during loading when possible? + if (Screen.Selected == GameMain.SubEditorScreen) + { + if (!MapEntity.SelectedAny) + { + ThrowError("You have to select item(s) first!"); + } + else + { + foreach (var mapEntity in MapEntity.SelectedList) + { + if (mapEntity is Item item) + { + item.Rect = new Rectangle(item.Rect.X, item.Rect.Y, + (int)(item.Prefab.sprite.size.X * item.Prefab.Scale), + (int)(item.Prefab.sprite.size.Y * item.Prefab.Scale)); + } + else if (mapEntity is Structure structure) + { + if (!structure.ResizeHorizontal) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + (int)structure.Prefab.ScaledSize.X, + structure.Rect.Height); + } + if (!structure.ResizeVertical) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + structure.Rect.Width, + (int)structure.Prefab.ScaledSize.Y); + } + } + } + } + } + }, isCheat: false)); +#endif + + GameMain.Config.SaveNewPlayerConfig(); + + commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => + { + string sourcePath = args.Length > 0 ? args[0] : "Content/Texts/EnglishVanilla.txt"; + string destinationPath = args.Length > 1 ? args[1] : "Content/Texts/EnglishVanilla.xml"; + + string[] lines; + try + { + lines = File.ReadAllLines(sourcePath); + } + catch (Exception e) + { + ThrowError("Reading the file \"" + sourcePath + "\" failed.", e); + return; + } + var doc = XMLExtensions.TryLoadXml(destinationPath); + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (i >= lines.Length) + { + ThrowError("Error while loading texts to the xml file. The xml has more elements than the number of lines in the text file."); + return; + } + element.Value = lines[i]; + i++; + } + doc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Select(f => f.Replace("\\", "/")); + return new string[][] + { + files.Where(f => Path.GetExtension(f)==".txt").ToArray(), + files.Where(f => Path.GetExtension(f)==".xml").ToArray() + }; + })); + + commands.Add(new Command("updatetextfile", "updatetextfile [sourcefile] [destinationfile]: Inserts all the xml elements that are only present in the source file into the destination file. Can be used to update outdated translation files more easily.", (string[] args) => + { + if (args.Length < 2) return; + string sourcePath = args[0]; + string destinationPath = args[1]; + + var sourceDoc = XMLExtensions.TryLoadXml(sourcePath); + var destinationDoc = XMLExtensions.TryLoadXml(destinationPath); + + XElement destinationElement = destinationDoc.Root.Elements().First(); + foreach (XElement element in sourceDoc.Root.Elements()) + { + if (destinationDoc.Root.Element(element.Name) == null) + { + element.Value = "!!!!!!!!!!!!!" + element.Value; + destinationElement.AddAfterSelf(element); + } + XNode nextNode = destinationElement.NextNode; + while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; + destinationElement = nextNode as XElement; + } + destinationDoc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Where(f => Path.GetExtension(f) == ".xml").Select(f => f.Replace("\\", "/")).ToArray(); + return new string[][] + { + files, + files + }; + })); + + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => + { + string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; + List lines = new List(); + foreach (MapEntityPrefab me in MapEntityPrefab.List) + { + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); + } + File.WriteAllLines(filePath, lines); + })); +#if DEBUG + commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => + { + if (args.Length != 1) return; + TextManager.CheckForDuplicates(args[0]); + })); + + commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) => + { + TextManager.WriteToCSV(); + NPCConversation.WriteToCSV(); + })); +#endif + + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); + + commands.Add(new Command("camerasettings", "camerasettings [defaultzoom] [zoomsmoothness] [movesmoothness] [minzoom] [maxzoom]: debug command for testing camera settings. The values default to 1.1, 8.0, 8.0, 0.1 and 2.0.", (string[] args) => + { + float defaultZoom = Screen.Selected.Cam.DefaultZoom; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out defaultZoom); + + float zoomSmoothness = Screen.Selected.Cam.ZoomSmoothness; + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out zoomSmoothness); + float moveSmoothness = Screen.Selected.Cam.MoveSmoothness; + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out moveSmoothness); + + float minZoom = Screen.Selected.Cam.MinZoom; + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out minZoom); + float maxZoom = Screen.Selected.Cam.MaxZoom; + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out maxZoom); + + Screen.Selected.Cam.DefaultZoom = defaultZoom; + Screen.Selected.Cam.ZoomSmoothness = zoomSmoothness; + Screen.Selected.Cam.MoveSmoothness = moveSmoothness; + Screen.Selected.Cam.MinZoom = minZoom; + Screen.Selected.Cam.MaxZoom = maxZoom; + })); + + commands.Add(new Command("waterparams", "waterparams [distortionscalex] [distortionscaley] [distortionstrengthx] [distortionstrengthy] [bluramount]: default 0.5 0.5 0.5 0.5 1", (string[] args) => + { + float distortScaleX = 0.5f, distortScaleY = 0.5f; + float distortStrengthX = 0.5f, distortStrengthY = 0.5f; + float blurAmount = 0.0f; + if (args.Length > 0) float.TryParse(args[0], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleX); + if (args.Length > 1) float.TryParse(args[1], NumberStyles.Number, CultureInfo.InvariantCulture, out distortScaleY); + if (args.Length > 2) float.TryParse(args[2], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthX); + if (args.Length > 3) float.TryParse(args[3], NumberStyles.Number, CultureInfo.InvariantCulture, out distortStrengthY); + if (args.Length > 4) float.TryParse(args[4], NumberStyles.Number, CultureInfo.InvariantCulture, out blurAmount); + WaterRenderer.DistortionScale = new Vector2(distortScaleX, distortScaleY); + WaterRenderer.DistortionStrength = new Vector2(distortStrengthX, distortStrengthY); + WaterRenderer.BlurAmount = blurAmount; + })); + + + commands.Add(new Command("refreshrect", "Updates the dimensions of the selected items to match the ones defined in the prefab. Applied only in the subeditor.", (string[] args) => + { + //TODO: maybe do this automatically during loading when possible? + if (Screen.Selected == GameMain.SubEditorScreen) + { + if (!MapEntity.SelectedAny) + { + ThrowError("You have to select item(s) first!"); + } + else + { + foreach (var mapEntity in MapEntity.SelectedList) + { + if (mapEntity is Item item) + { + item.Rect = new Rectangle(item.Rect.X, item.Rect.Y, + (int)(item.Prefab.sprite.size.X * item.Prefab.Scale), + (int)(item.Prefab.sprite.size.Y * item.Prefab.Scale)); + } + else if (mapEntity is Structure structure) + { + if (!structure.ResizeHorizontal) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + (int)structure.Prefab.ScaledSize.X, + structure.Rect.Height); + } + if (!structure.ResizeVertical) + { + structure.Rect = new Rectangle(structure.Rect.X, structure.Rect.Y, + structure.Rect.Width, + (int)structure.Prefab.ScaledSize.Y); + } + } + } + } + } + }, isCheat: false)); +#endif + + GameMain.Config.SaveNewPlayerConfig(); + + commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => + { + string sourcePath = args.Length > 0 ? args[0] : "Content/Texts/EnglishVanilla.txt"; + string destinationPath = args.Length > 1 ? args[1] : "Content/Texts/EnglishVanilla.xml"; + + string[] lines; + try + { + lines = File.ReadAllLines(sourcePath); + } + catch (Exception e) + { + ThrowError("Reading the file \"" + sourcePath + "\" failed.", e); + return; + } + var doc = XMLExtensions.TryLoadXml(destinationPath); + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (i >= lines.Length) + { + ThrowError("Error while loading texts to the xml file. The xml has more elements than the number of lines in the text file."); + return; + } + element.Value = lines[i]; + i++; + } + doc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Select(f => f.Replace("\\", "/")); + return new string[][] + { + files.Where(f => Path.GetExtension(f)==".txt").ToArray(), + files.Where(f => Path.GetExtension(f)==".xml").ToArray() + }; + })); + + commands.Add(new Command("updatetextfile", "updatetextfile [sourcefile] [destinationfile]: Inserts all the xml elements that are only present in the source file into the destination file. Can be used to update outdated translation files more easily.", (string[] args) => + { + if (args.Length < 2) return; + string sourcePath = args[0]; + string destinationPath = args[1]; + + var sourceDoc = XMLExtensions.TryLoadXml(sourcePath); + var destinationDoc = XMLExtensions.TryLoadXml(destinationPath); + + XElement destinationElement = destinationDoc.Root.Elements().First(); + foreach (XElement element in sourceDoc.Root.Elements()) + { + if (destinationDoc.Root.Element(element.Name) == null) + { + element.Value = "!!!!!!!!!!!!!" + element.Value; + destinationElement.AddAfterSelf(element); + } + XNode nextNode = destinationElement.NextNode; + while ((!(nextNode is XElement) || nextNode == element) && nextNode != null) nextNode = nextNode.NextNode; + destinationElement = nextNode as XElement; + } + destinationDoc.Save(destinationPath); + }, + () => + { + var files = TextManager.GetTextFiles().Where(f => Path.GetExtension(f) == ".xml").Select(f => f.Replace("\\", "/")).ToArray(); + return new string[][] + { + files, + files + }; + })); + + commands.Add(new Command("dumpentitytexts", "dumpentitytexts [filepath]: gets the names and descriptions of all entity prefabs and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EntityTexts.txt", (string[] args) => + { + string filePath = args.Length > 0 ? args[0] : "Content/Texts/EntityTexts.txt"; + List lines = new List(); + foreach (MapEntityPrefab me in MapEntityPrefab.List) + { + lines.Add("" + me.Name + ""); + lines.Add("" + me.Description + ""); + } + File.WriteAllLines(filePath, lines); + })); +#if DEBUG + commands.Add(new Command("checkduplicates", "Checks the given language for duplicate translation keys and writes to file.", (string[] args) => + { + if (args.Length != 1) return; + TextManager.CheckForDuplicates(args[0]); + })); + + commands.Add(new Command("writetocsv", "Writes the default language (English) to a .csv file.", (string[] args) => + { + TextManager.WriteToCSV(); + NPCConversation.WriteToCSV(); + })); commands.Add(new Command("loadtexts", "loadtexts [sourcefile] [destinationfile]: Loads all lines of text from a given .txt file and inserts them sequientially into the elements of an xml file. If the file paths are omitted, EnglishVanilla.txt and EnglishVanilla.xml are used.", (string[] args) => { diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs index 7b7a37b98..eec9c180b 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIButton.cs @@ -117,6 +117,12 @@ namespace Barotrauma set { textBlock.Text = value; } } + public bool ForceUpperCase + { + get { return textBlock.ForceUpperCase; } + set { textBlock.ForceUpperCase = value; } + } + public override string ToolTip { get diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs index e1063b2da..6cdba8652 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUIDropDown.cs @@ -112,11 +112,12 @@ namespace Barotrauma GUI.Style.Apply(button, "", this); listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, Anchor.BottomLeft, Pivot.TopLeft) - { IsFixedSize = false }, style: style) + { IsFixedSize = false }, style: null) { Enabled = !selectMultiple, OnSelected = SelectItem }; + GUI.Style.Apply(listBox.Content, "GUIListBox", this); currentListBoxParent = FindListBoxParent(); currentListBoxParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList; diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs index f55e4b318..e90fed400 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBlock.cs @@ -61,13 +61,15 @@ namespace Barotrauma get { return text; } set { - if (Text == value) return; + string newText = forceUpperCase ? value?.ToUpper() : value; + + if (Text == newText) return; //reset scale, it gets recalculated in SetTextPos - if (autoScale) textScale = 1.0f; + if (autoScale) textScale = 1.0f; - text = value; - wrappedText = value; + text = newText; + wrappedText = newText; SetTextPos(); } } @@ -120,6 +122,22 @@ namespace Barotrauma } } + private bool forceUpperCase; + public bool ForceUpperCase + { + get { return forceUpperCase; } + set + { + if (forceUpperCase == value) { return; } + + forceUpperCase = value; + if (forceUpperCase) + { + Text = text?.ToUpper(); + } + } + } + public Vector2 Origin { get { return origin; } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index 96c9396bf..870f3d553 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -66,6 +66,25 @@ namespace Barotrauma CanBeFocused = false }; + Point scrollButtonSize = new Point((int)(200 * GUI.Scale), (int)(30 * GUI.Scale)); + + crewArea = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform), "", Color.Transparent) + { + CanBeFocused = false + }; + toggleCrewButton = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform) + { AbsoluteOffset = HUDLayoutSettings.CrewArea.Location }, + "", style: "UIToggleButton"); + toggleCrewButton.OnClicked += (GUIButton btn, object userdata) => + { + toggleCrewAreaOpen = !toggleCrewAreaOpen; + foreach (GUIComponent child in btn.Children) + { + child.SpriteEffects = toggleCrewAreaOpen ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + } + return true; + }; + var characterInfo = new CharacterInfo(subElement); characterInfos.Add(characterInfo); foreach (XElement invElement in subElement.Elements()) @@ -76,54 +95,21 @@ namespace Barotrauma } } - var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); - reportButtonFrame = new GUILayoutGroup(new RectTransform( - new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)) - { - AbsoluteSpacing = (int)(5 * GUI.Scale), - UserData = "reportbuttons", - CanBeFocused = false - }; - - //report buttons - foreach (Order order in reports) - { - if (!order.TargetAllCharacters || order.SymbolSprite == null) continue; - var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null) - { - OnClicked = (GUIButton button, object userData) => - { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false; - SetCharacterOrder(null, order, null, Character.Controlled); - return true; - }, - UserData = order, - ToolTip = order.Name - }; - - new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") - { - Color = Color.Red * 0.8f, - HoverColor = Color.Red * 1.0f, - PressedColor = Color.Red * 0.6f, - UserData = "highlighted", - CanBeFocused = false, - Visible = false - }; - - var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite, scaleToFit: true) - { - Color = order.Color, - HoverColor = Color.Lerp(order.Color, Color.White, 0.5f), - ToolTip = order.Name - }; - } - screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); prevUIScale = GUI.Scale; } + + #endregion + + #region Character list management + + public Rectangle GetCharacterListArea() + { + return characterListBox.Rect; + } + partial void InitProjectSpecific() { guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent) @@ -1310,35 +1296,6 @@ namespace Barotrauma if (GameMain.NetworkMember != null) GameMain.Client.SelectCrewCharacter(character, previewPlayer); - bool hasIntruders = Character.CharacterList.Any(c => - c.CurrentHull == Character.Controlled.CurrentHull && !c.IsDead && - (c.AIController is EnemyAIController || (c.TeamID != Character.Controlled.TeamID && c.TeamID != Character.TeamType.FriendlyNPC))); - - ToggleReportButton("reportintruders", hasIntruders); - - foreach (GUIComponent reportButton in reportButtonFrame.Children) - { - var highlight = reportButton.GetChildByUserData("highlighted"); - if (highlight.Visible) - { - highlight.RectTransform.LocalScale = new Vector2(1.25f + (float)Math.Sin(Timing.TotalTime * 5.0f) * 0.25f); - } - } - } - else - { - reportButtonFrame.Visible = false; - } - } - - /// - /// Should report buttons be visible on the screen atm? - /// - private bool ReportButtonsVisible() - { - return CharacterHealth.OpenHealthWindow == null; - } - private bool ReportButtonClicked(GUIButton button, object userData) { //order targeted to all characters diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index 00085898f..a111ddc03 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -626,6 +626,28 @@ namespace Barotrauma new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); + return true; + }; + + //spacing + new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); + + new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft), + TextManager.Get("Cancel")) + { + IgnoreLayoutGroups = true, + OnClicked = (x, y) => + { + if (UnsavedSettings) + { + LoadPlayerConfig(); + } + if (Screen.Selected == GameMain.MainMenuScreen) GameMain.MainMenuScreen.ReturnToMainMenu(null, null); + GUI.SettingsMenuOpen = false; + return true; + } + }; + //spacing new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), generalLayoutGroup.RectTransform), style: null); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs index 72589ccf5..94a9eee32 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -184,6 +184,63 @@ namespace Barotrauma.Items.Components } } } + + public void ApplyTo(RectTransform target) + { + if (RelativeOffset.HasValue) + { + target.RelativeOffset = RelativeOffset.Value; + } + else if (AbsoluteOffset.HasValue) + { + target.AbsoluteOffset = AbsoluteOffset.Value; + } + if (RelativeSize.HasValue) + { + target.RelativeSize = RelativeSize.Value; + } + else if (AbsoluteSize.HasValue) + { + target.NonScaledSize = AbsoluteSize.Value; + } + if (Anchor.HasValue) + { + target.Anchor = Anchor.Value; + } + if (Pivot.HasValue) + { + target.Pivot = Pivot.Value; + } + else + { + target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor); + } + target.RecalculateChildren(true, true); + } + } + + public GUIFrame GuiFrame { get; protected set; } + + [Serialize(false, false)] + public bool AllowUIOverlap + { + get; + set; + } + + private ItemComponent linkToUIComponent; + [Serialize("", false)] + public string LinkUIToComponent + { + get; + set; + } + + [Serialize(0, false)] + public int HudPriority + { + get; + private set; } private bool shouldMuffleLooping; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index d167d6c12..52860e886 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -164,25 +164,25 @@ namespace Barotrauma Sprite activeSprite = prefab.sprite; BrokenItemSprite fadeInBrokenSprite = null; float fadeInBrokenSpriteAlpha = 0.0f; - if (condition < 100.0f) + if (condition < Prefab.Health) { for (int i = 0; i < Prefab.BrokenSprites.Count; i++) { - if (condition <= Prefab.BrokenSprites[i].MaxCondition) - { - activeSprite = Prefab.BrokenSprites[i].Sprite; - break; - } - if (Prefab.BrokenSprites[i].FadeIn) { - float min = i > 0 ? Prefab.BrokenSprites[i].MaxCondition : 0.0f; - float max = i < Prefab.BrokenSprites.Count - 1 ? Prefab.BrokenSprites[i + 1].MaxCondition : 100.0f; + float min = i > 0 ? Prefab.BrokenSprites[i - i].MaxCondition : 0.0f; + float max = Prefab.BrokenSprites[i].MaxCondition; fadeInBrokenSpriteAlpha = 1.0f - ((condition - min) / (max - min)); if (fadeInBrokenSpriteAlpha > 0.0f && fadeInBrokenSpriteAlpha < 1.0f) { fadeInBrokenSprite = Prefab.BrokenSprites[i]; } + continue; + } + if (condition <= Prefab.BrokenSprites[i].MaxCondition) + { + activeSprite = Prefab.BrokenSprites[i].Sprite; + break; } } } @@ -349,7 +349,6 @@ namespace Barotrauma GUI.DrawLine(spriteBatch, from, to, lineColor, width: 1); //GUI.DrawString(spriteBatch, from, $"Linked to {e.Name}", lineColor, Color.Black * 0.5f); } - } public void UpdateSpriteStates(float deltaTime) diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index 4d50e8e6a..06a90d84c 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -1918,10 +1918,13 @@ namespace Barotrauma.Networking /// /// Tell the server to select a submarine (permission required) /// - public void RequestSelectSub(int subIndex) + public void RequestSelectSub(int subIndex, bool isShuttle) { if (!HasPermission(ClientPermissions.SelectSub)) return; - if (subIndex < 0 || subIndex >= GameMain.NetLobbyScreen.SubList.Content.CountChildren) + + var subList = isShuttle ? GameMain.NetLobbyScreen.ShuttleList.ListBox : GameMain.NetLobbyScreen.SubList; + + if (subIndex < 0 || subIndex >= subList.Content.CountChildren) { DebugConsole.ThrowError("Submarine index out of bounds (" + subIndex + ")\n" + Environment.StackTrace); return; @@ -1930,6 +1933,7 @@ namespace Barotrauma.Networking NetOutgoingMessage msg = client.CreateMessage(); msg.Write((byte)ClientPacketHeader.SERVER_COMMAND); msg.Write((UInt16)ClientPermissions.SelectSub); + msg.Write(isShuttle); msg.WritePadBits(); msg.Write((UInt16)subIndex); msg.Write((byte)ServerNetObject.END_OF_MESSAGE); @@ -2203,8 +2207,11 @@ namespace Barotrauma.Networking if (EndVoteCount > 0) { - GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 180.0f, 40), - TextManager.Get("EndRoundVotes").Replace("[y]", EndVoteCount.ToString()).Replace("[n]", (EndVoteMax - EndVoteCount).ToString()), + string endVoteText = TextManager.Get("EndRoundVotes") + .Replace("[y]", EndVoteCount.ToString()) + .Replace("[n]", (EndVoteMax - EndVoteCount).ToString()); + GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 10.0f - GUI.SmallFont.MeasureString(endVoteText).X, 40), + endVoteText, Color.White, font: GUI.SmallFont); } diff --git a/Barotrauma/BarotraumaClient/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaClient/Source/Networking/ServerSettings.cs index e04e92461..335964b17 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/ServerSettings.cs @@ -142,7 +142,7 @@ namespace Barotrauma.Networking } } - public void ClientAdminWrite(NetFlags dataToSend, int missionType = 0, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0) + public void ClientAdminWrite(NetFlags dataToSend, int missionType = 0, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? useRespawnShuttle = null) { if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return; @@ -202,6 +202,8 @@ namespace Barotrauma.Networking outMsg.Write(levelDifficulty ?? -1000.0f); + outMsg.Write(useRespawnShuttle ?? UseRespawnShuttle); + outMsg.Write(autoRestart != null); outMsg.Write(autoRestart ?? false); outMsg.WritePadBits(); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index 316dd5656..f75d58b4d 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -438,43 +438,6 @@ namespace Barotrauma UpdateSourceRect(limb, newRect); } } - UpdateJointCreation(); - if (PlayerInput.KeyHit(Keys.Left)) - { - foreach (var limb in selectedLimbs) - { - var newRect = limb.ActiveSprite.SourceRect; - newRect.X--; - UpdateSourceRect(limb, newRect); - } - } - if (PlayerInput.KeyHit(Keys.Right)) - { - foreach (var limb in selectedLimbs) - { - var newRect = limb.ActiveSprite.SourceRect; - newRect.X++; - UpdateSourceRect(limb, newRect); - } - } - if (PlayerInput.KeyHit(Keys.Down)) - { - foreach (var limb in selectedLimbs) - { - var newRect = limb.ActiveSprite.SourceRect; - newRect.Y++; - UpdateSourceRect(limb, newRect); - } - } - if (PlayerInput.KeyHit(Keys.Up)) - { - foreach (var limb in selectedLimbs) - { - var newRect = limb.ActiveSprite.SourceRect; - newRect.Y--; - UpdateSourceRect(limb, newRect); - } - } } if (!isFreezed) { diff --git a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index 463ed3365..3dabc833a 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -238,6 +238,12 @@ namespace Barotrauma GameAnalyticsManager.SetCustomDimension01(""); } + public override void Deselect() + { + base.Deselect(); + SelectTab(null, 0); + } + private bool SelectTab(GUIButton button, object obj) { if (obj is Tab) diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index 7fa6c23aa..d965fd46a 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -132,11 +132,6 @@ namespace Barotrauma get { return shuttleList; } } - public GUITickBox ShuttleTickBox - { - get { return shuttleTickBox; } - } - public GUIListBox ModeList { get { return modeList; } @@ -190,7 +185,7 @@ namespace Barotrauma public bool UsingShuttle { get { return shuttleTickBox.Selected; } - set { shuttleTickBox.Selected = value; if (GameMain.Client != null) shuttleTickBox.Enabled = false; } + set { shuttleTickBox.Selected = value; } } public GameModePreset SelectedMode @@ -338,7 +333,7 @@ namespace Barotrauma var midInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.35f, 1.0f), infoColumnContainer.RectTransform, Anchor.BottomLeft)) { RelativeSpacing = 0.02f, Stretch = true }; - var rightInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.85f), infoFrameContent.RectTransform, Anchor.TopRight)) + var rightInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(0.3f, 0.9f), infoFrameContent.RectTransform, Anchor.TopRight)) { RelativeSpacing = 0.02f, Stretch = true }; var topButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), rightInfoColumn.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight) @@ -424,7 +419,15 @@ namespace Barotrauma OnSelected = (GUITickBox box) => { shuttleList.Enabled = box.Selected; - //if (GameMain.Server != null) lastUpdateID++; + GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, useRespawnShuttle: box.Selected); + return true; + } + }; + shuttleList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), elementCount: 10) + { + OnSelected = (component, obj) => + { + GameMain.Client.RequestSelectSub(component.Parent.GetChildIndex(component), isShuttle: true); return true; } }; @@ -622,7 +625,6 @@ namespace Barotrauma OnSelected = (tickBox) => { GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, autoRestart: tickBox.Selected); - return true; } }; @@ -633,6 +635,12 @@ namespace Barotrauma TextGetter = AutoRestartText }; + ReadyToStartBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.06f), rightInfoColumn.RectTransform), + TextManager.Get("ReadyToStartTickBox")) + { + Visible = false + }; + StartButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight), TextManager.Get("StartGameButton"), style: "GUIButtonLarge") { @@ -645,12 +653,6 @@ namespace Barotrauma }; clientHiddenElements.Add(StartButton); - ReadyToStartBox = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.06f), infoFrameContent.RectTransform, Anchor.BottomRight), - TextManager.Get("ReadyToStartTickBox"), GUI.SmallFont) - { - Visible = false - }; - campaignViewButton = new GUIButton(new RectTransform(new Vector2(0.3f, 0.1f), infoFrameContent.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.0f, 0.06f) }, TextManager.Get("CampaignView"), style: "GUIButtonLarge") { @@ -731,7 +733,7 @@ namespace Barotrauma if (GameMain.Client != null) { spectateButton.Visible = GameMain.Client.GameStarted; - ReadyToStartBox.Visible = !GameMain.Client.GameStarted && !GameMain.Client.HasPermission(ClientPermissions.ManageRound); + ReadyToStartBox.Visible = !GameMain.Client.GameStarted; ReadyToStartBox.Selected = false; GameMain.Client.SetReadyToStart(ReadyToStartBox); } @@ -841,10 +843,10 @@ namespace Barotrauma SettingsButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); SettingsButton.OnClicked = GameMain.Client.ServerSettings.ToggleSettingsFrame; - ReadyToStartBox.Visible = !GameMain.Client.HasPermission(ClientPermissions.ManageRound); StartButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ManageRound) && !campaignContainer.Visible; ServerName.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); ServerMessage.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); + shuttleTickBox.Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings); SubList.Enabled = GameMain.Client.ServerSettings.Voting.AllowSubVoting || GameMain.Client.HasPermission(ClientPermissions.SelectSub); ModeList.Enabled = GameMain.Client.ServerSettings.Voting.AllowModeVoting || GameMain.Client.HasPermission(ClientPermissions.SelectMode); ShowLogButton.Visible = GameMain.Client.HasPermission(ClientPermissions.ServerLog); @@ -1229,7 +1231,7 @@ namespace Barotrauma { if (GameMain.Client.HasPermission(ClientPermissions.SelectSub)) { - GameMain.Client.RequestSelectSub(component.Parent.GetChildIndex(component)); + GameMain.Client.RequestSelectSub(component.Parent.GetChildIndex(component), isShuttle: false); return true; } return false; @@ -1931,6 +1933,11 @@ namespace Barotrauma if (sub == null) sub = Submarine.SavedSubmarines.FirstOrDefault(m => m.Name == subName); var matchingListSub = subList.Content.GetChildByUserData(sub); + if (sub != null && subList.SelectedData as Submarine == sub) + { + return true; + } + if (matchingListSub != null) { if (subList.Parent is GUIDropDown subDropDown) diff --git a/Barotrauma/BarotraumaClient/Source/Utils/ToolBox.cs b/Barotrauma/BarotraumaClient/Source/Utils/ToolBox.cs index 9cd259906..46d79ca01 100644 --- a/Barotrauma/BarotraumaClient/Source/Utils/ToolBox.cs +++ b/Barotrauma/BarotraumaClient/Source/Utils/ToolBox.cs @@ -164,7 +164,7 @@ namespace Barotrauma if (i < words.Length - 1) wrappedText.Append(" "); } - return wrappedText.ToString(); + return wrappedText.ToString().Replace(" \n ", "\n"); } } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index dd114fd65..7e8ed91bd 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -1005,6 +1005,8 @@ namespace Barotrauma.Networking } break; case ClientPermissions.SelectSub: + bool isShuttle = inc.ReadBoolean(); + inc.ReadPadBits(); UInt16 subIndex = inc.ReadUInt16(); var subList = GameMain.NetLobbyScreen.GetSubList(); if (subIndex >= subList.Count) @@ -1013,7 +1015,14 @@ namespace Barotrauma.Networking } else { - GameMain.NetLobbyScreen.SelectedSub = subList[subIndex]; + if (isShuttle) + { + GameMain.NetLobbyScreen.SelectedShuttle = subList[subIndex]; + } + else + { + GameMain.NetLobbyScreen.SelectedSub = subList[subIndex]; + } } break; case ClientPermissions.SelectMode: @@ -1360,7 +1369,7 @@ namespace Barotrauma.Networking } outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.Name); outmsg.Write(GameMain.NetLobbyScreen.SelectedSub.MD5Hash.ToString()); - outmsg.Write(GameMain.NetLobbyScreen.UsingShuttle); + outmsg.Write(serverSettings.UseRespawnShuttle); outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name); outmsg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.ToString()); @@ -1462,7 +1471,6 @@ namespace Barotrauma.Networking Submarine selectedSub = null; Submarine selectedShuttle = GameMain.NetLobbyScreen.SelectedShuttle; - bool usingShuttle = GameMain.NetLobbyScreen.UsingShuttle; if (serverSettings.Voting.AllowSubVoting) { @@ -1493,7 +1501,7 @@ namespace Barotrauma.Networking } initiatedStartGame = true; - startGameCoroutine = CoroutineManager.StartCoroutine(InitiateStartGame(selectedSub, selectedShuttle, usingShuttle, selectedMode), "InitiateStartGame"); + startGameCoroutine = CoroutineManager.StartCoroutine(InitiateStartGame(selectedSub, selectedShuttle, serverSettings.UseRespawnShuttle, selectedMode), "InitiateStartGame"); return true; } @@ -1758,7 +1766,7 @@ namespace Barotrauma.Networking msg.Write(selectedSub.Name); msg.Write(selectedSub.MD5Hash.Hash); - msg.Write(GameMain.NetLobbyScreen.UsingShuttle); + msg.Write(serverSettings.UseRespawnShuttle); msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.Name); msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash); diff --git a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs index 4b6418fb6..a9a354d94 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/ServerSettings.cs @@ -145,6 +145,8 @@ namespace Barotrauma.Networking float levelDifficulty = incMsg.ReadFloat(); if (levelDifficulty >= 0.0f) SelectedLevelDifficulty = levelDifficulty; + UseRespawnShuttle = incMsg.ReadBoolean(); + bool changedAutoRestart = incMsg.ReadBoolean(); bool autoRestart = incMsg.ReadBoolean(); if (changedAutoRestart) diff --git a/Barotrauma/BarotraumaServer/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaServer/Source/Screens/NetLobbyScreen.cs index 17072a56e..6cea05941 100644 --- a/Barotrauma/BarotraumaServer/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaServer/Source/Screens/NetLobbyScreen.cs @@ -10,7 +10,6 @@ namespace Barotrauma { private Submarine selectedSub; private Submarine selectedShuttle; - private bool usingShuttle = true; public Submarine SelectedSub { @@ -22,17 +21,8 @@ namespace Barotrauma get { return selectedShuttle; } set { selectedShuttle = value; lastUpdateID++; } } - public bool UsingShuttle - { - get { return usingShuttle; } - set { usingShuttle = value; lastUpdateID++; } - } - private GameModePreset[] gameModes; - public GameModePreset[] GameModes - { - get { return gameModes; } - } + public GameModePreset[] GameModes { get; } private int selectedModeIndex; public int SelectedModeIndex @@ -41,18 +31,18 @@ namespace Barotrauma set { lastUpdateID++; - selectedModeIndex = MathHelper.Clamp(value, 0, gameModes.Length - 1); + selectedModeIndex = MathHelper.Clamp(value, 0, GameModes.Length - 1); } } public string SelectedModeIdentifier { - get { return gameModes[SelectedModeIndex].Identifier; } + get { return GameModes[SelectedModeIndex].Identifier; } set { - for (int i = 0; i < gameModes.Length; i++) + for (int i = 0; i < GameModes.Length; i++) { - if (gameModes[i].Identifier.ToLower() == value.ToLower()) + if (GameModes[i].Identifier.ToLower() == value.ToLower()) { SelectedModeIndex = i; break; @@ -63,7 +53,7 @@ namespace Barotrauma public GameModePreset SelectedMode { - get { return gameModes[SelectedModeIndex]; } + get { return GameModes[SelectedModeIndex]; } } private int missionTypeIndex; @@ -118,13 +108,26 @@ namespace Barotrauma throw new Exception("No submarines are available."); } - selectedSub = subs.First(s => !s.HasTag(SubmarineTag.Shuttle)); + selectedSub = subs.FirstOrDefault(s => !s.HasTag(SubmarineTag.Shuttle)); + if (selectedSub == null) + { + //no subs available, use a shuttle + DebugConsole.ThrowError("No full-size submarines available - choosing a shuttle as the main submarine."); + selectedSub = subs[0]; + } + selectedShuttle = subs.First(s => s.HasTag(SubmarineTag.Shuttle)); + if (selectedShuttle == null) + { + //no shuttles available, use a sub + DebugConsole.ThrowError("No shuttles available - choosing a full-size submarine as the shuttle."); + selectedShuttle = subs[0]; + } DebugConsole.NewMessage("Selected sub: " + SelectedSub.Name, Color.White); DebugConsole.NewMessage("Selected shuttle: " + SelectedShuttle.Name, Color.White); - gameModes = GameModePreset.List.ToArray(); + GameModes = GameModePreset.List.ToArray(); } private List subs; @@ -180,7 +183,7 @@ namespace Barotrauma } if (GameMain.Server.ServerSettings.ModeSelectionMode == SelectionMode.Random) { - var allowedGameModes = Array.FindAll(gameModes, m => !m.IsSinglePlayer && m.Identifier != "multiplayercampaign"); + var allowedGameModes = Array.FindAll(GameModes, m => !m.IsSinglePlayer && m.Identifier != "multiplayercampaign"); SelectedModeIdentifier = allowedGameModes[Rand.Range(0, allowedGameModes.Length)].Identifier; } } diff --git a/Barotrauma/BarotraumaShared/Icon.ico b/Barotrauma/BarotraumaShared/Icon.ico index 5b40528ea8c1e51011333b0bfc4c3a0417706bff..a31034fcd91631735d36fa5144c6fbd2ffbfd965 100644 GIT binary patch literal 102803 zcmeI52Ygh;_QyB9ZrW}-RX|YCKu{D22`Uh&L8OQvMM^>oCDcd~5Ckd0Q+f*^Xpj~W zeKsIG{o5ddf`X!giV_qa5koNqIRA5QGP@@y*({OhGvxC5eAzSEx%bYU-^|=H=gb-m z4hB~P{c|wXGSu@n7>1%fZCYWvfxClY9{#O$>!S372E$IXgQ0Qb!t|zk2E#W!am!jo zX+MKu##9G`AAZvQ5}XZ&hR-<|=5*~6YxE5B#9bTPKisApeM`90q5F>8gvazc+rG^M z-6tRW?{{Yw_MK4uqw`6p-f8*n;k&vWy9VKo~JnD4mqwCVkNUj@9>`-8@|5Qzn?iY@u>~(ENQfpS9c746U zFvx7cJsRdeVwl#%)6lq`gWHnb!r{9ymY7#SpT=M*i

2;TkJ$Za~L5k<&wU10$d9lDf=bo}Qi+}sa_=qL> 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 O@_f>^cy2fK@Baa+TZ=pZ literal 16958 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 znztceU9? 1.0f || character.IsRemotePlayer) + flipTimer += deltaTime; + if ((flipTimer > 0.5f && flipCooldown <= 0.0f) || character.IsRemotePlayer) { Flip(); if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror)) @@ -262,8 +263,13 @@ namespace Barotrauma Mirror(); } flipTimer = 0.0f; + flipCooldown = 1.0f; } } + else + { + flipTimer = 0.0f; + } } private bool CanDrag(Character target) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Health/DamageModifier.cs b/Barotrauma/BarotraumaShared/Source/Characters/Health/DamageModifier.cs index de9be0c49..c10c55bdb 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Health/DamageModifier.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Health/DamageModifier.cs @@ -69,7 +69,8 @@ namespace Barotrauma public bool MatchesAffliction(Affliction affliction) { - if (AfflictionIdentifiers.Length == 0) { return true; } + //if no identifiers or types have been defined, the damage modifier affects all afflictions + if (AfflictionIdentifiers.Length == 0 && AfflictionTypes.Length == 0) { return true; } foreach (string afflictionName in AfflictionIdentifiers) { diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index 961fe4333..ef55fe457 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -149,7 +149,7 @@ namespace Barotrauma commands.SelectMany(c => c.names).ToArray(), new string[0] }; - }, isCheat: true)); + })); commands.Add(new Command("items|itemlist", "itemlist: List all the item prefabs available for spawning.", (string[] args) => @@ -382,9 +382,8 @@ namespace Barotrauma banDuration = parsedBanDuration; } - ShowQuestionPrompt("Reason for kicking \"" + client.Name + "\"?", (reason) => - { - GameMain.NetworkMember.KickPlayer(client.Name, reason); + GameMain.NetworkMember.BanPlayer(client.Name, reason, false, banDuration); + }); }); })); diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs index 076edcd71..56a40ffce 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs @@ -69,6 +69,8 @@ namespace Barotrauma public int ParticleLimit { get; set; } + public int ParticleLimit { get; set; } + public float LightMapScale { get; set; } public bool SpecularityEnabled { get; set; } public bool ChromaticAberrationEnabled { get; set; } @@ -401,6 +403,8 @@ namespace Barotrauma AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f); + AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f); + AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", 0.5f); keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length]; @@ -1252,6 +1256,84 @@ namespace Barotrauma NewLineOnAttributes = true }; +#if CLIENT + if (Tutorial.Tutorials != null) + { + foreach (Tutorial tutorial in Tutorial.Tutorials) + { + if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Name)) + { + CompletedTutorialNames.Add(tutorial.Name); + } + } + } +#endif + var tutorialElement = new XElement("tutorials"); + foreach (string tutorialName in CompletedTutorialNames) + { + tutorialElement.Add(new XElement("Tutorial", new XAttribute("name", tutorialName))); + } + doc.Root.Add(tutorialElement); + + XmlWriterSettings settings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true, + NewLineOnAttributes = true + }; + +#if CLIENT + if (Tutorial.Tutorials != null) + { + foreach (Tutorial tutorial in Tutorial.Tutorials) + { + if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Name)) + { + CompletedTutorialNames.Add(tutorial.Name); + } + } + } +#endif + var tutorialElement = new XElement("tutorials"); + foreach (string tutorialName in CompletedTutorialNames) + { + tutorialElement.Add(new XElement("Tutorial", new XAttribute("name", tutorialName))); + } + doc.Root.Add(tutorialElement); + + XmlWriterSettings settings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true, + NewLineOnAttributes = true + }; + +#if CLIENT + if (Tutorial.Tutorials != null) + { + foreach (Tutorial tutorial in Tutorial.Tutorials) + { + if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Name)) + { + CompletedTutorialNames.Add(tutorial.Name); + } + } + } +#endif + var tutorialElement = new XElement("tutorials"); + foreach (string tutorialName in CompletedTutorialNames) + { + tutorialElement.Add(new XElement("Tutorial", new XAttribute("name", tutorialName))); + } + doc.Root.Add(tutorialElement); + + XmlWriterSettings settings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true, + NewLineOnAttributes = true + }; + #if CLIENT if (Tutorial.Tutorials != null) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs index 7d71b7d36..53317666a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs @@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components partial class Steering : Powered, IServerSerializable, IClientSerializable { private const float AutopilotRayCastInterval = 0.5f; - private const float RecalculatePathInterval = 10.0f; + private const float RecalculatePathInterval = 5.0f; private Vector2 currVelocity; private Vector2 targetVelocity; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs index c3d5d2926..5e0b5c4dc 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs @@ -430,6 +430,13 @@ namespace Barotrauma } CurrentLocation.SelectedMissionIndex = missionIndex; + //the destination must be the same as the destination of the mission + if (CurrentLocation.SelectedMission != null && + CurrentLocation.SelectedMission.Locations[1] != SelectedLocation) + { + SelectLocation(CurrentLocation.SelectedMission.Locations[1]); + } + SelectedLocation = location; SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation)); OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection); diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index 09ac31d59..763b0b250 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -992,14 +992,14 @@ namespace Barotrauma if (IsHorizontal) { diffFromCenter = (rect.Center.X - this.rect.Center.X) / (float)this.rect.Width * BodyWidth; - if (BodyWidth > 0.0f) rect.Width = (int)(BodyWidth * (rect.Width / (float)this.rect.Width)); + if (BodyWidth > 0.0f) rect.Width = Math.Max((int)Math.Round(BodyWidth * (rect.Width / (float)this.rect.Width)), 1); if (BodyHeight > 0.0f) rect.Height = (int)BodyHeight; } else { diffFromCenter = ((rect.Y - rect.Height / 2) - (this.rect.Y - this.rect.Height / 2)) / (float)this.rect.Height * BodyHeight; if (BodyWidth > 0.0f) rect.Width = (int)BodyWidth; - if (BodyHeight > 0.0f) rect.Height = (int)(BodyHeight * (rect.Height / (float)this.rect.Height)); + if (BodyHeight > 0.0f) rect.Height = Math.Max((int)Math.Round(BodyHeight * (rect.Height / (float)this.rect.Height)), 1); } if (FlippedX) diffFromCenter = -diffFromCenter; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 64f4ddb79..4d39310f9 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -304,7 +304,7 @@ namespace Barotrauma this.filePath = filePath; try { - name = System.IO.Path.GetFileNameWithoutExtension(filePath); + name = displayName = Path.GetFileNameWithoutExtension(filePath); } catch (Exception e) { diff --git a/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs b/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs index 253713f65..5732a9955 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/ServerSettings.cs @@ -321,6 +321,13 @@ namespace Barotrauma.Networking set; } + [Serialize(true, true)] + public bool UseRespawnShuttle + { + get; + private set; + } + [Serialize(300.0f, true)] public float RespawnInterval { @@ -342,7 +349,6 @@ namespace Barotrauma.Networking private set; } - [Serialize(60.0f, true)] public float AutoRestartInterval { diff --git a/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs b/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs index a179bfe57..8ae558c4d 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/Voting.cs @@ -1,8 +1,5 @@ using Barotrauma.Networking; -using Lidgren.Network; -using System; using System.Collections.Generic; -using System.Linq; namespace Barotrauma { @@ -41,7 +38,7 @@ namespace Barotrauma if (voteType == VoteType.Sub && !AllowSubVoting) return default(T); if (voteType == VoteType.Mode && !AllowModeVoting) return default(T); - List> voteList = GetVoteList(voteType,voters); + List> voteList = GetVoteList(voteType, voters); T selected = default(T); int highestVotes = 0; diff --git a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index dac0e7d44..fc56c8fc7 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -160,6 +160,16 @@ namespace Barotrauma } #endif + public void SetState() + { + hit = binding.IsHit(); + if (hit) hitQueue = true; + + held = binding.IsDown(); + if (held) heldQueue = true; + } +#endif + public bool Hit { get diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs index a267c2344..dd1fbb2e8 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs @@ -693,7 +693,10 @@ namespace Barotrauma if (target is Character character) { character.LastDamageSource = entity; - character.AddDamage(entity.WorldPosition, new List() { multipliedAffliction }, stun: 0.0f, playSound: false); + foreach (Limb limb in character.AnimController.Limbs) + { + limb.character.DamageLimb(entity.WorldPosition, limb, new List() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f); + } } else if (target is Limb limb) { @@ -704,9 +707,9 @@ namespace Barotrauma foreach (Pair reduceAffliction in ReduceAffliction) { float reduceAmount = disableDeltaTime ? reduceAffliction.Second : reduceAffliction.Second * deltaTime; - if (target is Character) + if (target is Character character) { - ((Character)target).CharacterHealth.ReduceAffliction(null, reduceAffliction.First, reduceAmount); + character.CharacterHealth.ReduceAffliction(null, reduceAffliction.First, reduceAmount); } else if (target is Limb limb) {