diff --git a/Barotrauma/BarotraumaClient/Icon.bmp b/Barotrauma/BarotraumaClient/Icon.bmp new file mode 100644 index 000000000..dedeee273 Binary files /dev/null and b/Barotrauma/BarotraumaClient/Icon.bmp differ diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 5bfa817fa..9f61fb02c 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -31,6 +31,7 @@ true + ..\BarotraumaShared\IconBmp.bmp ..\BarotraumaShared\Icon.ico @@ -103,6 +104,7 @@ PreserveNewest + PreserveNewest @@ -152,13 +154,16 @@ - + + + PreserveNewest + PreserveNewest @@ -187,6 +192,11 @@ + + + Icon.bmp + + diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 5fbe31943..29dd488d3 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -103,6 +103,7 @@ PreserveNewest + PreserveNewest @@ -159,6 +160,9 @@ + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs index 08da64291..def51c503 100644 --- a/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaClient/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.9.5")] -[assembly: AssemblyFileVersion("0.8.9.5")] +[assembly: AssemblyVersion("0.8.9.6")] +[assembly: AssemblyFileVersion("0.8.9.6")] diff --git a/Barotrauma/BarotraumaClient/Shaders/Content.mgcb b/Barotrauma/BarotraumaClient/Shaders/Content.mgcb new file mode 100644 index 000000000..cc09bad54 --- /dev/null +++ b/Barotrauma/BarotraumaClient/Shaders/Content.mgcb @@ -0,0 +1,57 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:Windows +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin blurshader.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:blurshader.fx + +#begin damageshader.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:damageshader.fx + +#begin deformshader.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:deformshader.fx + +#begin losshader.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:losshader.fx + +#begin postprocess.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:postprocess.fx + +#begin watershader.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:watershader.fx + +#begin solidcolor.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:solidcolor.fx + diff --git a/Barotrauma/BarotraumaClient/Shaders/Content_opengl.mgcb b/Barotrauma/BarotraumaClient/Shaders/Content_opengl.mgcb new file mode 100644 index 000000000..6606ae7ca --- /dev/null +++ b/Barotrauma/BarotraumaClient/Shaders/Content_opengl.mgcb @@ -0,0 +1,62 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin blurshader_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:blurshader_opengl.fx + +#begin Content_opengl.mgcb +/importer: +/processor: +/build:Content_opengl.mgcb + +#begin damageshader_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:damageshader_opengl.fx + +#begin deformshader_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:deformshader_opengl.fx + +#begin losshader_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:losshader_opengl.fx + +#begin postprocess_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:postprocess_opengl.fx + +#begin solidcolor_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:solidcolor_opengl.fx + +#begin watershader_opengl.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:watershader_opengl.fx + diff --git a/Barotrauma/BarotraumaClient/Shaders/solidcolor.fx b/Barotrauma/BarotraumaClient/Shaders/solidcolor.fx new file mode 100644 index 000000000..846f3d2fe --- /dev/null +++ b/Barotrauma/BarotraumaClient/Shaders/solidcolor.fx @@ -0,0 +1,38 @@ +Texture2D xTexture; +sampler TextureSampler = sampler_state { Texture = ; }; + +float blurDistance; +float4 color; + +float4 solidColor(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 +{ + float a = tex2D(TextureSampler, texCoord).a; + return color * a; +} + +float4 solidColorBlur(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 +{ + float sample; + sample = tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y + blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y - blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y - blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y + blurDistance)).a; + sample = sample * 0.25f; + + return color * sample; +} + +technique SolidColor +{ + pass Pass1 + { + PixelShader = compile ps_4_0_level_9_1 solidColor(); + } +} +technique SolidColorBlur +{ + pass Pass1 + { + PixelShader = compile ps_4_0_level_9_1 solidColorBlur(); + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/Shaders/solidcolor_opengl.fx b/Barotrauma/BarotraumaClient/Shaders/solidcolor_opengl.fx new file mode 100644 index 000000000..2c82b0bf3 --- /dev/null +++ b/Barotrauma/BarotraumaClient/Shaders/solidcolor_opengl.fx @@ -0,0 +1,38 @@ +Texture xTexture; +sampler TextureSampler = sampler_state { Texture = ; }; + +float blurDistance; +float4 color; + +float4 solidColor(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 +{ + float a = tex2D(TextureSampler, texCoord).a; + return color * a; +} + +float4 solidColorBlur(float4 position : SV_Position, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0 +{ + float sample; + sample = tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y + blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y - blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x + blurDistance, texCoord.y - blurDistance)).a; + sample += tex2D(TextureSampler, float2(texCoord.x - blurDistance, texCoord.y + blurDistance)).a; + sample = sample * 0.25f; + + return color * sample; +} + +technique SolidColor +{ + pass Pass1 + { + PixelShader = compile ps_3_0 solidColor(); + } +} +technique SolidColorBlur +{ + pass Pass1 + { + PixelShader = compile ps_3_0 solidColorBlur(); + } +} \ No newline at end of file diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index 86c88a3e0..fd0af8cf0 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -242,7 +242,7 @@ namespace Barotrauma GUI.Style.FocusIndicator.Draw(spriteBatch, (int)((focusedItemOverlayTimer - 1.0f) * GUI.Style.FocusIndicator.FrameCount * 3.0f), circlePos, - Color.Orange * 0.3f, + Color.LightBlue * 0.3f, origin: GUI.Style.FocusIndicator.FrameSize.ToVector2() / 2, rotate: (float)Timing.TotalTime, scale: scale); @@ -274,9 +274,7 @@ namespace Barotrauma GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont); textPos.Y += 20; } - } - - + } } foreach (HUDProgressBar progressBar in character.HUDProgressBars.Values) diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs index 4fb00dc00..75ea85fb8 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHealth.cs @@ -544,7 +544,10 @@ namespace Barotrauma { healthBar.Color = healthWindowHealthBar.Color = ToolBox.GradientLerp(Vitality / MaxVitality, Color.Red, Color.Orange, Color.Green); healthBar.HoverColor = healthWindowHealthBar.HoverColor = healthBar.Color * 2.0f; - healthBar.BarSize = healthWindowHealthBar.BarSize = (Vitality > 0.0f) ? Vitality / MaxVitality : 1.0f - Vitality / MinVitality; + healthBar.BarSize = healthWindowHealthBar.BarSize = + (Vitality > 0.0f) ? + (MaxVitality > 0.0f ? Vitality / MaxVitality : 0.0f) : + (Math.Abs(MinVitality) > 0.0f ? 1.0f - Vitality / MinVitality : 0.0f); if (healthBarPulsateTimer > 0.0f) { diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs index 4aac62ef9..54acd801e 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterNetworking.cs @@ -363,7 +363,7 @@ namespace Barotrauma character.ID = id; character.TeamID = (TeamType)teamID; - if (configPath == HumanConfigFile) + if (configPath == HumanConfigFile && character.TeamID != TeamType.FriendlyNPC) { CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID); GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo); diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index 6345fd8d7..43e6a9938 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -436,7 +436,7 @@ namespace Barotrauma AssignOnExecute("ambientlight", (string[] args) => { - Color color = XMLExtensions.ParseColor(string.Join("", args)); + Color color = XMLExtensions.ParseColor(string.Join(",", args)); if (Level.Loaded != null) { Level.Loaded.GenerationParams.AmbientLightColor = color; @@ -1060,2559 +1060,6 @@ namespace Barotrauma })); #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(); - })); -#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(); - })); -#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(); - })); -#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(); - })); -#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) => - { - 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("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; - }, - null, null)); - - - 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 - commands.Add(new Command("cleanbuild", "", (string[] args) => { GameMain.Config.MusicVolume = 0.5f; diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index 3f6d853ee..ded8f0896 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -210,12 +210,7 @@ namespace Barotrauma public void SetWindowMode(WindowMode windowMode) { WindowMode = windowMode; -#if !(OSX) GraphicsDeviceManager.HardwareModeSwitch = Config.WindowMode != WindowMode.BorderlessWindowed; -#else - // Force borderless on macOS. - GraphicsDeviceManager.HardwareModeSwitch = Config.WindowMode != WindowMode.BorderlessWindowed && Config.WindowMode != WindowMode.Fullscreen; -#endif GraphicsDeviceManager.IsFullScreen = Config.WindowMode == WindowMode.Fullscreen || Config.WindowMode == WindowMode.BorderlessWindowed; Window.IsBorderless = !GraphicsDeviceManager.HardwareModeSwitch; diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index a5968ce0f..1ef20b629 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -61,120 +61,9 @@ namespace Barotrauma public CrewManager(XElement element, bool isSinglePlayer) : this(isSinglePlayer) { - guiFrame = new GUIFrame(new RectTransform(Vector2.One, GUICanvas.Instance), null, Color.Transparent) + foreach (XElement subElement in element.Elements()) { - 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; - }; - - characterListBox = new GUIListBox(new RectTransform(new Point(100, (int)(crewArea.Rect.Height - scrollButtonSize.Y * 1.6f)), crewArea.RectTransform, Anchor.CenterLeft), false, Color.Transparent, null) - { - //Spacing = (int)(3 * GUI.Scale), - ScrollBarEnabled = false, - ScrollBarVisible = false, - CanBeFocused = false - }; - - scrollButtonUp = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.TopLeft, Pivot.TopLeft), "", Alignment.Center, "GUIButtonVerticalArrow") - { - Visible = false, - UserData = -1, - OnClicked = ScrollCharacterList - }; - scrollButtonDown = new GUIButton(new RectTransform(scrollButtonSize, crewArea.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft), "", Alignment.Center, "GUIButtonVerticalArrow") - { - Visible = false, - UserData = 1, - OnClicked = ScrollCharacterList - }; - scrollButtonDown.Children.ForEach(c => c.SpriteEffects = SpriteEffects.FlipVertically); - - if (isSinglePlayer) - { - chatBox = new ChatBox(guiFrame, isSinglePlayer: true) - { - OnEnterMessage = (textbox, text) => - { - if (Character.Controlled == null) { return true; } - - textbox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default]; - - if (!string.IsNullOrWhiteSpace(text)) - { - string msgCommand = ChatMessage.GetChatMessageCommand(text, out string msg); - AddSinglePlayerChatMessage( - Character.Controlled.Info.Name, - msg, - ((msgCommand == "r" || msgCommand == "radio") && ChatMessage.CanUseRadio(Character.Controlled)) ? ChatMessageType.Radio : ChatMessageType.Default, - Character.Controlled); - var headset = GetHeadset(Character.Controlled, true); - if (headset != null && headset.CanTransmit()) - { - headset.TransmitSignal(stepsTaken: 0, signal: msg, source: headset.Item, sender: Character.Controlled, sendToChat: false); - } - } - textbox.Deselect(); - textbox.Text = ""; - return true; - } - }; - - chatBox.InputBox.OnTextChanged += chatBox.TypingChatMessage; - } - - var reports = Order.PrefabList.FindAll(o => o.TargetAllCharacters && o.SymbolSprite != null); - reportButtonFrame = new GUILayoutGroup(new RectTransform( - new Point((HUDLayoutSettings.CrewArea.Height - (int)((reports.Count - 1) * 5 * GUI.Scale)) / reports.Count, HUDLayoutSettings.CrewArea.Height), guiFrame.RectTransform)) - { - AbsoluteSpacing = (int)(5 * GUI.Scale), - UserData = "reportbuttons", - CanBeFocused = false - }; - - //report buttons - foreach (Order order in reports) - { - if (!order.TargetAllCharacters || order.SymbolSprite == null) continue; - var btn = new GUIButton(new RectTransform(new Point(reportButtonFrame.Rect.Width), reportButtonFrame.RectTransform), style: null) - { - OnClicked = (GUIButton button, object userData) => - { - if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false; - SetCharacterOrder(null, order, null, Character.Controlled); - HumanAIController.PropagateHullSafety(Character.Controlled, Character.Controlled.CurrentHull); - return true; - }, - UserData = order, - ToolTip = order.Name - }; - - new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") - { - Color = Color.Red * 0.8f, - HoverColor = Color.Red * 1.0f, - PressedColor = Color.Red * 0.6f, - UserData = "highlighted", - CanBeFocused = false, - Visible = false - }; + if (subElement.Name.ToString().ToLowerInvariant() != "character") continue; var characterInfo = new CharacterInfo(subElement); characterInfos.Add(characterInfo); @@ -185,20 +74,6 @@ namespace Barotrauma break; } } - - screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight); - - prevUIScale = GUI.Scale; - } - - - #endregion - - #region Character list management - - public Rectangle GetCharacterListArea() - { - return characterListBox.Rect; } partial void InitProjectSpecific() @@ -713,7 +588,7 @@ namespace Barotrauma characterListBox.BarScroll -= characterListBox.BarScroll % step; characterListBox.BarScroll += dir * step; - return radioItem.GetComponent(); + return false; } private IEnumerable KillCharacterAnim(GUIComponent component) @@ -726,12 +601,6 @@ namespace Barotrauma { comp.Color = Color.DarkRed; } - List availableSpeakers = Character.CharacterList.FindAll(c => - c.AIController is HumanAIController && - !c.IsDead && - c.SpeechImpediment <= 100.0f); - pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers)); - } yield return new WaitForSeconds(1.0f); @@ -1384,16 +1253,6 @@ namespace Barotrauma if (GameMain.NetworkMember != null) GameMain.Client.SelectCrewCharacter(character, previewPlayer); - private bool ReportButtonClicked(GUIButton button, object userData) - { - //order targeted to all characters - Order order = userData as Order; - if (order.TargetAllCharacters) - { - if (Character.Controlled == null || Character.Controlled.CurrentHull == null) return false; - AddOrder(new Order(order.Prefab, Character.Controlled.CurrentHull, null), order.Prefab.FadeOutTime); - SetCharacterOrder(null, order, "", Character.Controlled); - } return true; } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs index b3c2fdc42..0ab86bef6 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -4,6 +4,7 @@ using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; namespace Barotrauma { @@ -229,7 +230,10 @@ namespace Barotrauma campaign.lastUpdateID = updateID; } } + + public override void Save(XElement element) + { + //do nothing, the clients get the save files from the server + } } - - } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/GameSession.cs b/Barotrauma/BarotraumaClient/Source/GameSession/GameSession.cs index 16a57092d..2950e988f 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/GameSession.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/GameSession.cs @@ -141,9 +141,6 @@ namespace Barotrauma infoFrame?.UpdateManually(deltaTime); } - infoFrame?.UpdateManually(deltaTime); - } - public void Draw(SpriteBatch spriteBatch) { if (GUI.DisableHUD) return; diff --git a/Barotrauma/BarotraumaClient/Source/GameSettings.cs b/Barotrauma/BarotraumaClient/Source/GameSettings.cs index 00085898f..4ee4ab87e 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSettings.cs @@ -47,7 +47,7 @@ namespace Barotrauma //if "%" is found if (index > 0) { - while (true) + while (index > 0) { //search for end of label index -= 1; @@ -439,7 +439,6 @@ namespace Barotrauma } }; #endif - var radioButtonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), voiceSettings.RectTransform)) { Stretch = true, @@ -458,6 +457,24 @@ namespace Barotrauma voiceMode.AddRadioButton((VoiceMode)i, tick); } + var micVolumeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.18f), voiceSettings.RectTransform), TextManager.Get("MicrophoneVolume")); + var micVolumeSlider = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.18f), voiceSettings.RectTransform), + barSize: 0.1f) + { + UserData = micVolumeText, + BarScroll = (float)Math.Sqrt(MathUtils.InverseLerp(0.2f, 5.0f, MicrophoneVolume)), + OnMoved = (scrollBar, scroll) => + { + MicrophoneVolume = MathHelper.Lerp(0.2f, 5.0f, scroll * scroll); + MicrophoneVolume = (float)Math.Round(MicrophoneVolume, 1); + ChangeSliderText(scrollBar, MicrophoneVolume); + scrollBar.Step = 0.05f; + return true; + }, + Step = 0.05f + }; + micVolumeSlider.OnMoved(micVolumeSlider, micVolumeSlider.BarScroll); + var voiceInputContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.2f), voiceSettings.RectTransform, Anchor.BottomCenter)); new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), voiceInputContainer.RectTransform), TextManager.Get("InputType.Voice") + ": "); var voiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), voiceInputContainer.RectTransform, Anchor.TopRight), @@ -469,7 +486,6 @@ namespace Barotrauma voiceKeyBox.SelectedColor = Color.Gold * 0.3f; var voiceActivityGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), voiceSettings.RectTransform)); - GUITextBlock noiseGateText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), voiceActivityGroup.RectTransform), TextManager.Get("NoiseGateThreshold")) { TextGetter = () => @@ -509,13 +525,13 @@ namespace Barotrauma if (GameMain.Client == null && VoipCapture.Instance == null) { VoipCapture.Create(GameMain.Config.VoiceCaptureDevice); - } - if (VoipCapture.Instance == null) - { - VoiceSetting = vMode = VoiceMode.Disabled; - voiceInputContainer.Visible = false; - voiceActivityGroup.Visible = false; - return; + if (VoipCapture.Instance == null) + { + VoiceSetting = vMode = VoiceMode.Disabled; + voiceInputContainer.Visible = false; + voiceActivityGroup.Visible = false; + return; + } } } else @@ -615,16 +631,6 @@ namespace Barotrauma .Replace("[missingfiletypes]", string.Join(", ", missingContentTypes)); } } - languageDD.SelectItem(TextManager.Language); - languageDD.OnSelected = (guiComponent, obj) => - { - string newLanguage = obj as string; - if (newLanguage == Language) return true; - - UnsavedSettings = true; - Language = newLanguage; - - new GUIMessageBox(TextManager.Get("RestartRequiredLabel"), TextManager.Get("RestartRequiredLanguage")); //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..c6732589d 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/ItemComponent.cs @@ -25,15 +25,6 @@ namespace Barotrauma.Items.Components public readonly ActionType Type; public string VolumeProperty; - public float VolumeMultiplier - { - get { return RoundSound.Volume; } - } - - public float Range - { - get { return RoundSound.Range; } - } public float VolumeMultiplier { @@ -198,20 +189,20 @@ namespace Barotrauma.Items.Components { if (loopingSoundChannel != null) { - loopingSoundChannel.Dispose(); loopingSoundChannel = null; + loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; } return; } if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound) { - loopingSoundChannel.Dispose(); loopingSoundChannel = null; + loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; } if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying) { loopingSoundChannel = loopingSound.RoundSound.Sound.Play( new Vector3(position.X, position.Y, 0.0f), - GetSoundVolume(loopingSound), + 0.01f, SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull)); loopingSoundChannel.Looping = true; //TODO: tweak @@ -226,7 +217,9 @@ namespace Barotrauma.Items.Components lastMuffleCheckTime = (float)Timing.TotalTime; } loopingSoundChannel.Muffled = shouldMuffleLooping; - loopingSoundChannel.Gain = GetSoundVolume(loopingSound); + float targetGain = GetSoundVolume(loopingSound); + float gainDiff = targetGain - loopingSoundChannel.Gain; + loopingSoundChannel.Gain += Math.Abs(gainDiff) < 0.1f ? gainDiff : Math.Sign(gainDiff) * 0.1f; loopingSoundChannel.Position = new Vector3(position.X, position.Y, 0.0f); } return; @@ -279,13 +272,13 @@ namespace Barotrauma.Items.Components loopingSound = itemSound; if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound) { - loopingSoundChannel.Dispose(); loopingSoundChannel = null; + loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; } if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying) { loopingSoundChannel = loopingSound.RoundSound.Sound.Play( new Vector3(position.X, position.Y, 0.0f), - GetSoundVolume(loopingSound), + 0.01f, muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull)); loopingSoundChannel.Looping = true; //TODO: tweak @@ -309,7 +302,7 @@ namespace Barotrauma.Items.Components if (loopingSoundChannel != null) { - loopingSoundChannel.Dispose(); + loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null; loopingSound = null; } diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs index 68d2a9b90..19882d141 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Reactor.cs @@ -392,12 +392,12 @@ namespace Barotrauma.Items.Components Vector2 pos = new Vector2(rect.Center.X, rect.Y + meterSprite.Origin.Y * scale); Vector2 optimalRangeNormalized = new Vector2( - (optimalRange.X - range.X) / (range.Y - range.X), - (optimalRange.Y - range.X) / (range.Y - range.X)); + MathHelper.Clamp((optimalRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f), + MathHelper.Clamp((optimalRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f)); Vector2 allowedRangeNormalized = new Vector2( - (allowedRange.X - range.X) / (range.Y - range.X), - (allowedRange.Y - range.X) / (range.Y - range.X)); + MathHelper.Clamp((allowedRange.X - range.X) / (range.Y - range.X), 0.0f, 0.95f), + MathHelper.Clamp((allowedRange.Y - range.X) / (range.Y - range.X), 0.0f, 1.0f)); Vector2 sectorRad = new Vector2(-1.57f, 1.57f); @@ -417,10 +417,10 @@ namespace Barotrauma.Items.Components { spriteBatch.End(); Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle; - spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0,0,GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale)); + spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3); spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable); - sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2, scale); + sectorSprite.Draw(spriteBatch, pos, Color.LightGreen, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale); sectorSprite.Draw(spriteBatch, pos, Color.Orange, optimalSectorRad.X, scale); sectorSprite.Draw(spriteBatch, pos, Color.Red, allowedSectorRad.X, scale); diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs index 99072f14b..4ed31820f 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Turret.cs @@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components { if (moveSound != null) { - moveSoundChannel.Dispose(); + moveSoundChannel.FadeOutAndDispose(); moveSoundChannel = SoundPlayer.PlaySound(moveSound.Sound, moveSound.Volume, moveSound.Range, item.WorldPosition); if (moveSoundChannel != null) moveSoundChannel.Looping = true; } @@ -168,13 +168,13 @@ namespace Barotrauma.Items.Components { if (endMoveSound != null && moveSoundChannel.Sound != endMoveSound.Sound) { - moveSoundChannel.Dispose(); + moveSoundChannel.FadeOutAndDispose(); moveSoundChannel = SoundPlayer.PlaySound(endMoveSound.Sound, endMoveSound.Volume, endMoveSound.Range, item.WorldPosition); if (moveSoundChannel != null) moveSoundChannel.Looping = false; } else if (!moveSoundChannel.IsPlaying) { - moveSoundChannel.Dispose(); + moveSoundChannel.FadeOutAndDispose(); moveSoundChannel = null; } @@ -288,7 +288,10 @@ namespace Barotrauma.Items.Components availableAmmo.AddRange(itemContainer.Inventory.Items); } - float chargeRate = powerConsumption <= 0.0f ? 1.0f : batteryCharge / batteryCapacity; + float chargeRate = + powerConsumption <= 0.0f ? + 1.0f : + batteryCapacity > 0.0f ? batteryCharge / batteryCapacity : 0.0f; bool charged = batteryCharge * 3600.0f > powerConsumption; bool readyToFire = reload <= 0.0f && charged && availableAmmo.Any(p => p != null); if (ShowChargeIndicator && PowerConsumption > 0.0f) diff --git a/Barotrauma/BarotraumaClient/Source/Items/Item.cs b/Barotrauma/BarotraumaClient/Source/Items/Item.cs index 2f1a0c511..40af52153 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Item.cs @@ -158,7 +158,7 @@ namespace Barotrauma if (!Visible || (!editing && hiddenInGame)) return; if (editing && !ShowItems) return; - Color color = isHighlighted && !GUI.DisableItemHighlights ? Color.Orange : GetSpriteColor(); + Color color = isHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? Color.Orange : GetSpriteColor(); //if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f); Sprite activeSprite = prefab.sprite; @@ -410,8 +410,7 @@ namespace Barotrauma return true; } } - } - + } } public override void UpdateEditing(Camera cam) @@ -483,17 +482,44 @@ namespace Barotrauma linkText.TextColor = Color.Yellow; itemsText.TextColor = Color.Yellow; } - if (!inGame && Sprite != null) + + if (!inGame) { - var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite")); - reloadTextureButton.OnClicked += (button, data) => + var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, 20)), isHorizontal: true) { - Sprite.ReloadXML(); - Sprite.ReloadTexture(); - return true; + Stretch = true, + RelativeSpacing = 0.02f }; - itemEditor.AddCustomContent(reloadTextureButton, itemEditor.ContentCount); - } + new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX")) + { + ToolTip = TextManager.Get("MirrorEntityXToolTip"), + OnClicked = (button, data) => + { + FlipX(relativeToSub: false); + return true; + } + }; + new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY")) + { + ToolTip = TextManager.Get("MirrorEntityYToolTip"), + OnClicked = (button, data) => + { + FlipY(relativeToSub: false); + return true; + } + }; + if (Sprite != null) + { + var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite")); + reloadTextureButton.OnClicked += (button, data) => + { + Sprite.ReloadXML(); + Sprite.ReloadTexture(); + return true; + }; + } + itemEditor.AddCustomContent(buttonContainer, itemEditor.ContentCount); + } foreach (ItemComponent ic in components) { diff --git a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs index 5f0b72f2e..609974556 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Hull.cs @@ -599,61 +599,5 @@ namespace Barotrauma } } } - - public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime) - { - float newWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume; - float newOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); - - bool hasFireSources = message.ReadBoolean(); - int fireSourceCount = 0; - List newFireSources = new List(); - if (hasFireSources) - { - fireSourceCount = message.ReadRangedInteger(0, 16); - for (int i = 0; i < fireSourceCount; i++) - { - newFireSources.Add(new Vector3( - MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), - MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f), - message.ReadRangedSingle(0.0f, 1.0f, 8))); - } - } - - if (serverUpdateDelay > 0.0f) { return; } - - WaterVolume = newWaterVolume; - OxygenPercentage = newOxygenPercentage; - - for (int i = 0; i < fireSourceCount; i++) - { - Vector2 pos = new Vector2( - rect.X + rect.Width * newFireSources[i].X, - rect.Y - rect.Height + (rect.Height * newFireSources[i].Y)); - float size = newFireSources[i].Z * rect.Width; - - var newFire = i < FireSources.Count ? - FireSources[i] : - new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true); - newFire.Position = pos; - newFire.Size = new Vector2(size, newFire.Size.Y); - - //ignore if the fire wasn't added to this room (invalid position)? - if (!FireSources.Contains(newFire)) - { - newFire.Remove(); - continue; - } - } - - for (int i = FireSources.Count - 1; i >= fireSourceCount; i--) - { - FireSources[i].Remove(); - if (i < FireSources.Count) - { - FireSources.RemoveAt(i); - } - } - } } } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelObjects/LevelObject.cs b/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelObjects/LevelObject.cs index 61fccf5b8..aca975434 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelObjects/LevelObject.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Levels/LevelObjects/LevelObject.cs @@ -211,7 +211,7 @@ namespace Barotrauma } else if (SoundChannels[i] != null && SoundChannels[i].IsPlaying) { - SoundChannels[i].Dispose(); + SoundChannels[i].FadeOutAndDispose(); SoundChannels[i] = null; } } diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs index fcc45f7dc..c61e3821d 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightManager.cs @@ -43,11 +43,19 @@ namespace Barotrauma.Lights get; private set; } + public RenderTarget2D HighlightMap + { + get; + private set; + } + + private readonly Texture2D highlightRaster; private BasicEffect lightEffect; public Effect LosEffect { get; private set; } - + public Effect SolidColorEffect { get; private set; } + private List lights; public bool LosEnabled = true; @@ -58,7 +66,6 @@ namespace Barotrauma.Lights public bool ObstructVision; private Texture2D visionCircle; - private Dictionary hullAmbientLights; private Dictionary smoothedHullAmbientLights; @@ -73,6 +80,8 @@ namespace Barotrauma.Lights visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png"); + highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png"); + CreateRenderTargets(graphics); GameMain.Instance.OnResolutionChanged += () => { @@ -81,8 +90,10 @@ namespace Barotrauma.Lights #if WINDOWS LosEffect = content.Load("Effects/losshader"); + SolidColorEffect = content.Load("Effects/solidcolor"); #else LosEffect = content.Load("Effects/losshader_opengl"); + SolidColorEffect = content.Load("Effects/solidcolor_opengl"); #endif if (lightEffect == null) @@ -117,6 +128,12 @@ namespace Barotrauma.Lights pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount, RenderTargetUsage.DiscardContents); + HighlightMap?.Dispose(); + HighlightMap = new RenderTarget2D(graphics, + (int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale), (int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false, + pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount, + RenderTargetUsage.DiscardContents); + LosTexture?.Dispose(); LosTexture = new RenderTarget2D(graphics, (int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale), (int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false, SurfaceFormat.Color, DepthFormat.None); } @@ -176,7 +193,7 @@ namespace Barotrauma.Lights public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null) { if (!LightingEnabled) return; - + if (Math.Abs(currLightMapScale - GameMain.Config.LightMapScale) > 0.01f) { //lightmap scale has changed -> recreate render targets @@ -187,6 +204,8 @@ namespace Barotrauma.Lights Matrix transform = cam.ShaderTransform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f; + bool highlightsVisible = UpdateHighlights(graphics, spriteBatch, spriteBatchTransform, cam); + if (GameMain.Config.SpecularityEnabled) { UpdateSpecularMap(graphics, spriteBatch, spriteBatchTransform, cam, backgroundObstructor); @@ -211,7 +230,6 @@ namespace Barotrauma.Lights graphics.Clear(Color.Black); graphics.BlendState = BlendState.Additive; - //draw background lights //--------------------------------------------------------------------------------------------------- bool backgroundSpritesDrawn = false; @@ -260,31 +278,7 @@ namespace Barotrauma.Lights //draw the focused item and character to highlight them, //and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites) //--------------------------------------------------------------------------------------------------- - spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); - - if (Character.Controlled != null) - { - if (Character.Controlled.FocusedItem != null) - { - Character.Controlled.FocusedItem.IsHighlighted = true; - } - if (Character.Controlled.FocusedCharacter != null) - { - Character.Controlled.FocusedCharacter.Draw(spriteBatch, cam); - } - - if (!GUI.DisableItemHighlights) - { - foreach (Item item in Item.ItemList) - { - if (item.IsHighlighted) - { - item.Draw(spriteBatch, false, true); - } - } - } - } - + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); foreach (LightSource light in activeLights) { if (light.IsBackground) continue; @@ -292,6 +286,13 @@ namespace Barotrauma.Lights } spriteBatch.End(); + if (highlightsVisible) + { + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive); + spriteBatch.Draw(HighlightMap, Vector2.Zero, Color.White); + spriteBatch.End(); + } + //draw characters to obstruct the highlighted items/characters and light sprites //--------------------------------------------------------------------------------------------------- spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, transformMatrix: spriteBatchTransform); @@ -377,13 +378,95 @@ namespace Barotrauma.Lights graphics.BlendState = BlendState.AlphaBlend; } + private readonly List highlightedEntities = new List(); + + private bool UpdateHighlights(GraphicsDevice graphics, SpriteBatch spriteBatch, Matrix spriteBatchTransform, Camera cam) + { + if (GUI.DisableItemHighlights) { return false; } + + highlightedEntities.Clear(); + if (Character.Controlled != null) + { + if (Character.Controlled.FocusedItem != null) + { + highlightedEntities.Add(Character.Controlled.FocusedItem); + } + if (Character.Controlled.FocusedCharacter != null) + { + highlightedEntities.Add(Character.Controlled.FocusedCharacter); + } + foreach (Item item in Item.ItemList) + { + if (item.IsHighlighted && !highlightedEntities.Contains(item)) + { + highlightedEntities.Add(item); + } + } + } + if (highlightedEntities.Count == 0) { return false; } + + //draw characters in light blue first + graphics.SetRenderTarget(HighlightMap); + SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"]; + SolidColorEffect.Parameters["color"].SetValue(Color.LightBlue.ToVector4()); + SolidColorEffect.CurrentTechnique.Passes[0].Apply(); + DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidColor"]; + DeformableSprite.Effect.Parameters["solidColor"].SetValue(Color.LightBlue.ToVector4()); + DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply(); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform); + foreach (Entity highlighted in highlightedEntities) + { + if (highlighted is Item item) + { + item.Draw(spriteBatch, false, true); + } + else if (highlighted is Character character) + { + character.Draw(spriteBatch, cam); + } + } + spriteBatch.End(); + + //draw characters in black with a bit of blur, leaving the white edges visible + float phase = (float)(Math.Sin(Timing.TotalTime * 3.0f) + 1.0f) / 2.0f; //phase oscillates between 0 and 1 + Vector4 overlayColor = Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase); + SolidColorEffect.Parameters["color"].SetValue(overlayColor); + SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColorBlur"]; + SolidColorEffect.CurrentTechnique.Passes[0].Apply(); + DeformableSprite.Effect.Parameters["solidColor"].SetValue(overlayColor); + DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply(); + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform); + foreach (Entity highlighted in highlightedEntities) + { + if (highlighted is Item item) + { + SolidColorEffect.Parameters["blurDistance"].SetValue(0.02f); + item.Draw(spriteBatch, false, true); + } + else if (highlighted is Character character) + { + SolidColorEffect.Parameters["blurDistance"].SetValue(0.05f); + character.Draw(spriteBatch, cam); + } + } + spriteBatch.End(); + + //raster pattern on top of everything + spriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.LinearWrap); + spriteBatch.Draw(highlightRaster, new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height), Color.White * 0.5f); + spriteBatch.End(); + + DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"]; + + return true; + } public void UpdateSpecularMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Matrix spriteBatchTransform, Camera cam, RenderTarget2D backgroundObstructor = null) { graphics.SetRenderTarget(SpecularMap); //clear the lightmap - graphics.Clear(Color.Black); + graphics.Clear(Color.Gray); graphics.BlendState = BlendState.AlphaBlend; spriteBatch.Begin(sortMode: SpriteSortMode.Deferred, blendState: BlendState.AlphaBlend, transformMatrix: spriteBatchTransform); @@ -394,20 +477,31 @@ namespace Barotrauma.Lights } spriteBatch.End(); - //TODO: specular maps for level walls Level.Loaded?.Renderer?.RenderWalls(graphics, cam, specular: true); - spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); + DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidColor"]; + DeformableSprite.Effect.Parameters["solidColor"].SetValue(Color.Gray.ToVector4()); + DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply(); + + //obstruct specular maps behind the sub and characters by drawing them on the map in solid gray + SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"]; + SolidColorEffect.Parameters["color"].SetValue(Color.Gray.ToVector4()); + SolidColorEffect.CurrentTechnique.Passes[0].Apply(); + spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: SolidColorEffect); if (backgroundObstructor != null) { spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0, - (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black); + (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.White); + } + foreach (Character c in Character.CharacterList) + { + if (c.Enabled) { c.Draw(spriteBatch, cam); } } - GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, - (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), - Color.White * 0.4f, isFilled: true); spriteBatch.End(); + + DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"]; + graphics.SetRenderTarget(null); graphics.BlendState = BlendState.AlphaBlend; } diff --git a/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs b/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs index 1f4a3bbd7..e8a491973 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/MapEntity.cs @@ -549,7 +549,6 @@ namespace Barotrauma if ((PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))) { - //TODO: a UI button for flipping entities if (PlayerInput.KeyHit(Keys.N)) { float minX = selectedList[0].WorldRect.X, maxX = selectedList[0].WorldRect.Right; diff --git a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs index 65cfd37c9..bf0d8126a 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs @@ -92,15 +92,41 @@ namespace Barotrauma editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this }; GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null); var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, elementHeight: 20); - var reloadTextureButton = new GUIButton(new RectTransform(new Point(editingHUD.Rect.Width / 2, 20)), TextManager.Get("ReloadSprite")); - reloadTextureButton.OnClicked += (button, data) => - { - Sprite.ReloadXML(); - Sprite.ReloadTexture(); - return true; - }; - editor.AddCustomContent(reloadTextureButton, editor.ContentCount); + var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, 20)), isHorizontal: true) + { + Stretch = true, + RelativeSpacing = 0.02f + }; + new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX")) + { + ToolTip = TextManager.Get("MirrorEntityXToolTip"), + OnClicked = (button, data) => + { + FlipX(relativeToSub: false); + return true; + } + }; + new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY")) + { + ToolTip = TextManager.Get("MirrorEntityYToolTip"), + OnClicked = (button, data) => + { + FlipY(relativeToSub: false); + return true; + } + }; + var reloadTextureButton = new GUIButton(new RectTransform(new Vector2(0.3f, 1.0f), buttonContainer.RectTransform), TextManager.Get("ReloadSprite")) + { + OnClicked = (button, data) => + { + Sprite.ReloadXML(); + Sprite.ReloadTexture(); + return true; + } + }; + editor.AddCustomContent(buttonContainer, editor.ContentCount); + PositionEditingHUD(); return editingHUD; diff --git a/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs b/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs index 982066597..0e5dc2aed 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Submarine.cs @@ -405,6 +405,11 @@ namespace Barotrauma errorMsgs.Add(TextManager.Get("NoBallastTagsWarning")); } + if (Gap.GapList.Any(g => g.linkedTo.Count == 0)) + { + errorMsgs.Add(TextManager.Get("NonLinkedGapsWarning")); + } + if (errorMsgs.Any()) { new GUIMessageBox(TextManager.Get("Warning"), string.Join("\n\n", errorMsgs), 400, 0); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs index 98b7cd3bc..deebab3d5 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/GameClient.cs @@ -84,7 +84,7 @@ namespace Barotrauma.Networking { get { return fileReceiver; } } - + public bool MidRoundSyncing { get { return entityEventManager.MidRoundSyncing; } @@ -321,18 +321,17 @@ namespace Barotrauma.Networking // Loop until we are approved //TODO: show the name of the server instead of IP when connecting through the server list (more streamer-friendly) - string connectingText = TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP); + string connectingText = TextManager.Get("Connecting"); while (!CanStart && !connectCancelled) { if (reconnectBox == null) { - reconnectBox = new GUIMessageBox(TextManager.Get("Connecting"), connectingText, new string[] { TextManager.Get("Cancel") }); - + reconnectBox = new GUIMessageBox(connectingText, TextManager.Get("ConnectingTo").Replace("[serverip]", serverIP), new string[] { TextManager.Get("Cancel") }); reconnectBox.Buttons[0].OnClicked += CancelConnect; reconnectBox.Buttons[0].OnClicked += reconnectBox.Close; } - reconnectBox.Text.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1)); + reconnectBox.Header.Text = connectingText + new string('.', ((int)Timing.TotalTime % 3 + 1)); if (DateTime.Now > reqAuthTime) { @@ -540,6 +539,7 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.Select(); } connected = true; + chatBox.InputBox.Enabled = true; if (GameMain.NetLobbyScreen?.TextBox != null) { GameMain.NetLobbyScreen.TextBox.Enabled = true; @@ -832,6 +832,8 @@ namespace Barotrauma.Networking DisconnectReason disconnectReason = DisconnectReason.Unknown; if (splitMsg.Length > 0) Enum.TryParse(splitMsg[0], out disconnectReason); + DebugConsole.NewMessage("Received a disconnect message (" + disconnectMsg + ")"); + if (disconnectReason == DisconnectReason.ServerFull) { //already waiting for a slot to free up, do nothing @@ -861,22 +863,19 @@ namespace Barotrauma.Networking waitInServerQueueBox = null; CoroutineManager.StopCoroutines("WaitInServerQueue"); } - else - { - string msg = ""; - if (disconnectReason == DisconnectReason.Unknown) - { - msg = disconnectMsg; - } - else - { - msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString()); if (allowReconnect && disconnectReason == DisconnectReason.Unknown) { + DebugConsole.NewMessage("Attempting to reconnect..."); + + string msg = TextManager.GetServerMessage(disconnectMsg); + msg = string.IsNullOrWhiteSpace(msg) ? + TextManager.Get("ConnectionLostReconnecting") : + msg + '\n' + TextManager.Get("ConnectionLostReconnecting"); + reconnectBox = new GUIMessageBox( TextManager.Get("ConnectionLost"), - TextManager.Get("ConnectionLostReconnecting"), new string[0]); + msg, new string[0]); connected = false; ConnectToServer(serverIP); } @@ -885,10 +884,12 @@ namespace Barotrauma.Networking string msg = ""; if (disconnectReason == DisconnectReason.Unknown) { + DebugConsole.NewMessage("Do not attempt reconnect (not allowed)."); msg = disconnectMsg; } else { + DebugConsole.NewMessage("Do not attempt to reconnect (DisconnectReason doesn't allow reconnection)."); msg = TextManager.Get("DisconnectReason." + disconnectReason.ToString()); for (int i = 1; i < splitMsg.Length; i++) @@ -923,25 +924,6 @@ namespace Barotrauma.Networking } yield return new WaitForSeconds(0.5f); } - } - - waitInServerQueueBox?.Close(); - waitInServerQueueBox = null; - - yield return CoroutineStatus.Success; - } - - - private void ReadAchievement(NetIncomingMessage inc) - { - string achievementIdentifier = inc.ReadString(); - SteamAchievementManager.UnlockAchievement(achievementIdentifier); - } - - private void ReadPermissions(NetIncomingMessage inc) - { - List permittedConsoleCommands = new List(); - byte clientID = inc.ReadByte(); waitInServerQueueBox?.Close(); waitInServerQueueBox = null; @@ -1192,7 +1174,7 @@ namespace Barotrauma.Networking gameStarted = inc.ReadBoolean(); bool allowSpectating = inc.ReadBoolean(); - + ReadPermissions(inc); if (gameStarted) @@ -1324,12 +1306,6 @@ namespace Barotrauma.Networking byte botCount = inc.ReadByte(); BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal; - byte botCount = inc.ReadByte(); - BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal; - - byte botCount = inc.ReadByte(); - BotSpawnMode botSpawnMode = inc.ReadBoolean() ? BotSpawnMode.Fill : BotSpawnMode.Normal; - bool autoRestartEnabled = inc.ReadBoolean(); float autoRestartTimer = autoRestartEnabled ? inc.ReadFloat() : 0.0f; @@ -1496,7 +1472,7 @@ namespace Barotrauma.Networking prevBytePos = inc.PositionInBytes; } } - + private void SendLobbyUpdate() { NetOutgoingMessage outmsg = client.CreateMessage(); diff --git a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs index 2f85beaf6..bd1df6c87 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/Voip/VoipCapture.cs @@ -1,4 +1,5 @@ using Lidgren.Network; +using Microsoft.Xna.Framework; using OpenTK.Audio.OpenAL; using System; using System.Linq; @@ -26,6 +27,11 @@ namespace Barotrauma.Networking get; private set; } + + public float Gain + { + get { return GameMain.Config?.MicrophoneVolume ?? 1.0f; } + } public DateTime LastEnqueueAudio; @@ -69,6 +75,7 @@ namespace Barotrauma.Networking { if (!GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "capturedevicenotfound")) { + GUI.SettingsMenuOpen = false; new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("VoipCaptureDeviceNotFound")) { UserData = "capturedevicenotfound" @@ -160,8 +167,9 @@ namespace Barotrauma.Networking double maxAmplitude = 0.0f; for (int i = 0; i < VoipConfig.BUFFER_SIZE; i++) { - double sampleVal = (double)uncompressedBuffer[i] / (double)short.MaxValue; - maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal)); + uncompressedBuffer[i] = (short)MathHelper.Clamp((uncompressedBuffer[i] * Gain), -short.MaxValue, short.MaxValue); + double sampleVal = uncompressedBuffer[i] / (double)short.MaxValue; + maxAmplitude = Math.Max(maxAmplitude, Math.Abs(sampleVal)); } double dB = Math.Min(20 * Math.Log10(maxAmplitude), 0.0); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs index ce3420604..48be1e5a0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CharacterEditorScreen.cs @@ -117,7 +117,6 @@ namespace Barotrauma GameMain.Instance.OnResolutionChanged += OnResolutionChanged; instance = this; } - #endregion public override void Deselect() { @@ -196,6 +195,7 @@ namespace Barotrauma { ParamsEditor.Instance.EditorBox.AddToGUIUpdateList(); } + } public override void Update(double deltaTime) { @@ -351,91 +351,6 @@ namespace Barotrauma { CopyLimb(selectedLimb); } - idToCodeName.TryGetValue(id, out string notes); - LimbXElements.Add(id.ToString(), new XElement("limb", - new XAttribute("id", id), - new XAttribute("name", limbName), - new XAttribute("type", limbType.ToString()), - colliderAttributes, - new XElement("sprite", - new XAttribute("texture", TexturePath), - new XAttribute("sourcerect", $"{rectInputs[0].IntValue}, {rectInputs[1].IntValue}, {width}, {height}")), - new XAttribute("notes", null ?? string.Empty) - )); - } - } - 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); - } - } - 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); } } UpdateJointCreation(); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/GameScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/GameScreen.cs index 2246c630a..a16eb2187 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/GameScreen.cs @@ -51,10 +51,6 @@ namespace Barotrauma damageEffect.Parameters["xStencil"].SetValue(damageStencil); damageEffect.Parameters["aMultiplier"].SetValue(50.0f); damageEffect.Parameters["cMultiplier"].SetValue(200.0f); - - distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png"); - postProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture); - } distortTexture = TextureLoader.FromFile("Content/Effects/distortnormals.png"); postProcessEffect.Parameters["xDistortTexture"].SetValue(distortTexture); diff --git a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index bb123c052..091909577 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -1,15 +1,15 @@ -using Barotrauma.Networking; +using Barotrauma.Extensions; +using Barotrauma.Networking; using Barotrauma.Tutorials; +using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; -using System.IO; using System.Collections.Generic; -using System.Linq; -using Barotrauma.Extensions; using System.Diagnostics; -using Lidgren.Network; +using System.IO; +using System.Linq; using System.Threading; namespace Barotrauma @@ -34,10 +34,13 @@ namespace Barotrauma private Tab selectedTab; private Sprite backgroundSprite; + private Sprite backgroundVignette; #region Creation public MainMenuScreen(GameMain game) { + backgroundVignette = new Sprite("Content/UI/MainMenuVignette.png", Vector2.Zero); + new GUIImage(new RectTransform(new Vector2(0.35f, 0.2f), Frame.RectTransform, Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f, 0.05f), AbsoluteOffset = new Point(-5, -5) }, style: "TitleText") @@ -688,6 +691,11 @@ namespace Barotrauma blurAmount: 0.0f, aberrationStrength: 0.0f); } + + spriteBatch.Begin(blendState: BlendState.AlphaBlend); + backgroundVignette.Draw(spriteBatch, Vector2.Zero, Color.White, Vector2.Zero, 0.0f, + new Vector2(GameMain.GraphicsWidth / backgroundVignette.size.X, GameMain.GraphicsHeight / backgroundVignette.size.Y)); + spriteBatch.End(); } public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) diff --git a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs index b84cbd3ac..c8882034b 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/NetLobbyScreen.cs @@ -431,7 +431,6 @@ namespace Barotrauma return true; } }; - shuttleList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.05f), midInfoColumn.RectTransform), elementCount: 10); //gamemode ------------------------------------------------------------------ @@ -1050,10 +1049,9 @@ namespace Barotrauma } } - public void SetPlayYourself(bool playYourself) + public bool TogglePlayYourself(GUITickBox tickBox) { - this.playYourself.Selected = playYourself; - if (playYourself) + if (tickBox.Selected) { UpdatePlayerFrame(campaignCharacterInfo, allowEditing: campaignCharacterInfo == null); } @@ -1068,6 +1066,7 @@ namespace Barotrauma TextManager.Get("PlayingAsSpectator"), textAlignment: Alignment.Center); } + return false; } public void SetPlayYourself(bool playYourself) @@ -1097,7 +1096,6 @@ namespace Barotrauma { return; } - } //show the player config menu if spectating is not allowed if (!playYourself.Selected && !allowSpectating) diff --git a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs index aca7905bb..3fa7dcc26 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/ServerListScreen.cs @@ -716,7 +716,7 @@ namespace Barotrauma catch (PingException ex) { string errorMsg = "Failed to ping a server (" + serverInfo.ServerName + ", " + serverInfo.IP + ") - " + (ex?.InnerException?.Message ?? ex.Message); - GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("ServerListScreen.PingServer:PingException" + serverInfo.IP, GameAnalyticsSDK.Net.EGAErrorSeverity.Warning, errorMsg); #if DEBUG DebugConsole.NewMessage(errorMsg, Color.Red); #endif diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index 022395eb3..7ac15f8d0 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -1913,8 +1913,6 @@ namespace Barotrauma CreateUI(); } - if (tutorial != null) tutorial.Update((float)deltaTime); - hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull); saveAssemblyFrame.Visible = MapEntity.SelectedList.Count > 0; diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/OggSound.cs b/Barotrauma/BarotraumaClient/Source/Sounds/OggSound.cs index 6d69f3a12..dacc02b24 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/OggSound.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/OggSound.cs @@ -26,7 +26,7 @@ namespace Barotrauma.Sounds if (!stream) { - int bufferSize = (int)reader.TotalSamples*reader.Channels; + int bufferSize = (int)reader.TotalSamples * reader.Channels; float[] floatBuffer = new float[bufferSize]; short[] shortBuffer = new short[bufferSize]; @@ -82,7 +82,7 @@ namespace Barotrauma.Sounds { if (!muffleFilters.TryGetValue(sampleRate, out BiQuad filter)) { - filter = new LowpassFilter(sampleRate, 400); + filter = new LowpassFilter(sampleRate, 800); muffleFilters.Add(sampleRate, filter); } filter.Process(buffer); diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundChannel.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundChannel.cs index 7a7e40194..e939f97bd 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundChannel.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundChannel.cs @@ -176,7 +176,7 @@ namespace Barotrauma.Sounds get { return gain; } set { - gain = Math.Max(Math.Min(value,1.0f),0.0f); + gain = Math.Max(Math.Min(value, 1.0f), 0.0f); if (ALSourceIndex < 0) return; @@ -432,7 +432,13 @@ namespace Barotrauma.Sounds this.Category = category; } } - + + public bool FadingOutAndDisposing; + public void FadeOutAndDispose() + { + FadingOutAndDisposing = true; + } + public void Dispose() { lock (mutex) diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundManager.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundManager.cs index 4256482ea..14807564c 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundManager.cs @@ -15,8 +15,8 @@ namespace Barotrauma.Sounds public bool Disabled { - Default = 0, - Voice = 1 + get; + private set; } private IntPtr alcDevice; @@ -109,12 +109,6 @@ namespace Barotrauma.Sounds { get { return loadedSounds.Select(s => s.Filename).Distinct().Count(); } } - public int UniqueLoadedSoundCount - { - get { return loadedSounds.Select(s => s.Filename).Distinct().Count(); } - } - - private Dictionary> categoryModifiers; private Dictionary> categoryModifiers; @@ -322,7 +316,6 @@ namespace Barotrauma.Sounds } return count; } -#endif public SoundChannel GetChannelFromSound(Sound sound) { @@ -420,15 +413,6 @@ namespace Barotrauma.Sounds { categoryModifiers[category].Second = muffle; } - else - { - categoryModifiers[category].Second = muffle; - } - else - { - categoryModifiers[category].Second = muffle; - } - } for (int i = 0; i < playingChannels.Length; i++) { @@ -477,7 +461,8 @@ namespace Barotrauma.Sounds { for (int j = 0; j < playingChannels[i].Length; j++) { - if (playingChannels[i][j] != null && playingChannels[i][j].IsStream) + if (playingChannels[i][j] == null) { continue; } + if (playingChannels[i][j].IsStream) { if (playingChannels[i][j].IsPlaying) { @@ -489,10 +474,18 @@ namespace Barotrauma.Sounds playingChannels[i][j].Dispose(); } } + else if (playingChannels[i][j].FadingOutAndDisposing) + { + playingChannels[i][j].Gain -= 0.1f; + if (playingChannels[i][j].Gain <= 0.0f) + { + playingChannels[i][j].Dispose(); + } + } } } } - Thread.Sleep(50); //TODO: use a separate thread for network audio? + Thread.Sleep(10); //TODO: use a separate thread for network audio? } } diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs index 6d34dd17e..5609902ab 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs @@ -215,19 +215,19 @@ namespace Barotrauma for (int i = 0; i < waterAmbienceChannels.Length; i++) { if (waterAmbienceChannels[i] == null) continue; - waterAmbienceChannels[i].Dispose(); + waterAmbienceChannels[i].FadeOutAndDispose(); waterAmbienceChannels[i] = null; } for (int i = 0; i < FlowSounds.Count; i++) { if (flowSoundChannels[i] == null) continue; - flowSoundChannels[i].Dispose(); + flowSoundChannels[i].FadeOutAndDispose(); flowSoundChannels[i] = null; } for (int i = 0; i < fireSoundChannels.Length; i++) { if (fireSoundChannels[i] == null) continue; - fireSoundChannels[i].Dispose(); + fireSoundChannels[i].FadeOutAndDispose(); fireSoundChannels[i] = null; } fireVolumeLeft[0] = 0.0f; fireVolumeLeft[1] = 0.0f; @@ -414,7 +414,7 @@ namespace Barotrauma { if (fireSoundChannels[i] != null) { - fireSoundChannels[i].Dispose(); + fireSoundChannels[i].FadeOutAndDispose(); fireSoundChannels[i] = null; } } diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs b/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs index e9bc2e862..3e86fcc2c 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/VoipSound.cs @@ -34,7 +34,7 @@ namespace Barotrauma.Sounds private static BiQuad[] muffleFilters = new BiQuad[] { - new LowpassFilter(VoipConfig.FREQUENCY, 400) + new LowpassFilter(VoipConfig.FREQUENCY, 800) }; private static BiQuad[] radioFilters = new BiQuad[] { diff --git a/Barotrauma/BarotraumaClient/Source/Sprite/DeformableSprite.cs b/Barotrauma/BarotraumaClient/Source/Sprite/DeformableSprite.cs index 860522283..54f33dbb6 100644 --- a/Barotrauma/BarotraumaClient/Source/Sprite/DeformableSprite.cs +++ b/Barotrauma/BarotraumaClient/Source/Sprite/DeformableSprite.cs @@ -26,6 +26,10 @@ namespace Barotrauma private int subDivX, subDivY; private static Effect effect; + public static Effect Effect + { + get { return effect; } + } private Point spritePos; private Point spriteSize; diff --git a/Barotrauma/BarotraumaClient/Source/Sprite/Sprite.cs b/Barotrauma/BarotraumaClient/Source/Sprite/Sprite.cs index 32b7c3e31..0fc1be586 100644 --- a/Barotrauma/BarotraumaClient/Source/Sprite/Sprite.cs +++ b/Barotrauma/BarotraumaClient/Source/Sprite/Sprite.cs @@ -279,7 +279,6 @@ namespace Barotrauma //check if another sprite is using the same texture if (!string.IsNullOrEmpty(FilePath)) //file can be empty if the sprite is created directly from a Texture2D instance { - string normalizedFilePath = Path.GetFullPath(file); foreach (Sprite s in list) { if (s.FullPath == FullPath) return; diff --git a/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs b/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs index e908621bc..67e912b03 100644 --- a/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs +++ b/Barotrauma/BarotraumaClient/Source/Utils/LocalizationCSVtoXML.cs @@ -1,4 +1,5 @@ #if DEBUG +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -14,10 +15,16 @@ namespace Barotrauma private const string conversationsPath = "Content/NPCConversations"; private const string infoTextPath = "Content/Texts"; - private const string xmlHeader = ""; + private const string xmlHeader = ""; public static void Convert(string language) { + if (TextManager.Language != "English") + { + DebugConsole.ThrowError("Use the english localization when converting .csv to allow copying values"); + return; + } + List conversationFiles = new List(); List infoTextFiles = new List(); @@ -36,6 +43,11 @@ namespace Barotrauma for (int i = 0; i < conversationFiles.Count; i++) { List xmlContent = ConvertConversationsToXML(File.ReadAllLines(conversationFiles[i], Encoding.UTF8), language); + if (xmlContent == null) + { + DebugConsole.ThrowError("NPCConversation Localization .csv to .xml conversion failed for: " + conversationFiles[i]); + continue; + } string xmlFileFullPath = $"{conversationsPath}/NPCConversations_{language}_NEW.xml"; File.WriteAllLines(xmlFileFullPath, xmlContent); DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath); @@ -44,6 +56,11 @@ namespace Barotrauma for (int i = 0; i < infoTextFiles.Count; i++) { List xmlContent = ConvertInfoTextToXML(File.ReadAllLines(infoTextFiles[i], Encoding.UTF8), language); + if (xmlContent == null) + { + DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + infoTextFiles[i]); + continue; + } string xmlFileFullPath = $"{infoTextPath}/{language}Vanilla_NEW.xml"; File.WriteAllLines(xmlFileFullPath, xmlContent); DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath); @@ -61,9 +78,8 @@ namespace Barotrauma xmlContent.Add(xmlHeader); xmlContent.Add($""); - xmlContent.Add(string.Empty); - for (int i = 0; i < csvContent.Length; i++) + for (int i = 1; i < csvContent.Length; i++) // Start at one to ignore header { csvContent[i] = csvContent[i].Trim(separator); @@ -105,7 +121,35 @@ namespace Barotrauma xmlContent.Add($""); xmlContent.Add(string.Empty); + xmlContent.Add(""); + + int traitStart = -1; for (int i = 0; i < csvContent.Length; i++) + { + if (csvContent[i].StartsWith("Personality")) + { + traitStart = i + 1; + break; + } + } + + if (traitStart == -1) + { + DebugConsole.ThrowError("Invalid formatting of NPCConversations, no traits found!"); + return null; + } + + for (int i = 0; i < NPCPersonalityTrait.List.Count; i++) // Traits + { + string[] split = SplitCSV(csvContent[traitStart + i].Trim(separator)); + xmlContent.Add( + $""); + } + + for (int i = traitStart + NPCPersonalityTrait.List.Count; i < csvContent.Length; i++) // Conversations { string[] split = SplitCSV(csvContent[i]); diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 2c4bc8e82..7cf32db5d 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -172,6 +172,7 @@ + @@ -252,6 +253,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs index 3dc185ec4..eb0a60828 100644 --- a/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs +++ b/Barotrauma/BarotraumaServer/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.8.9.5")] -[assembly: AssemblyFileVersion("0.8.9.5")] +[assembly: AssemblyVersion("0.8.9.6")] +[assembly: AssemblyFileVersion("0.8.9.6")] diff --git a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs index 7ea8f65a2..0656ea051 100644 --- a/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -3,11 +3,14 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; +using System.Xml.Linq; namespace Barotrauma { partial class MultiPlayerCampaign : CampaignMode { + private List characterData = new List(); + public static void StartNewCampaign(string savePath, string subName, string seed) { if (string.IsNullOrWhiteSpace(savePath)) return; @@ -202,5 +205,32 @@ namespace Barotrauma CargoManager.PurchaseItem(pi.ItemPrefab, pi.Quantity); } } + + public override void Save(XElement element) + { + XElement modeElement = new XElement("MultiPlayerCampaign", + new XAttribute("money", Money), + new XAttribute("cheatsenabled", CheatsEnabled)); + Map.Save(modeElement); + element.Add(modeElement); + + //save character data to a separate file + string characterDataPath = GetCharacterDataSavePath(); + XDocument characterDataDoc = new XDocument(new XElement("CharacterData")); + foreach (CharacterCampaignData cd in characterData) + { + characterDataDoc.Root.Add(cd.Save()); + } + try + { + characterDataDoc.Save(characterDataPath); + } + catch (Exception e) + { + DebugConsole.ThrowError("Saving multiplayer campaign characters to \"" + characterDataPath + "\" failed!", e); + } + + lastSaveID++; + } } } diff --git a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs index 666aad960..4c4641540 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/GameServer.cs @@ -1897,14 +1897,7 @@ namespace Barotrauma.Networking public override void AddChatMessage(ChatMessage message) { if (string.IsNullOrEmpty(message.Text)) { return; } - if (message.Sender != null) - { - Log($"{message.Sender}: {message.Text}", ServerLog.MessageType.Chat); - } - else - { - Log($"{message.Text}", ServerLog.MessageType.Chat); - } + Log(message.TextWithSender, ServerLog.MessageType.Chat); base.AddChatMessage(message); } @@ -2367,6 +2360,8 @@ namespace Barotrauma.Networking public void SendVoteStatus(List recipients) { + if (!recipients.Any()) { return; } + NetOutgoingMessage msg = server.CreateMessage(); msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY); msg.Write((byte)ServerNetObject.VOTE); @@ -2419,7 +2414,10 @@ namespace Barotrauma.Networking recipients.Add(otherClient.Connection); } } - server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0); + if (recipients.Any()) + { + server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0); + } serverSettings.SaveClientPermissions(); } @@ -2453,13 +2451,15 @@ namespace Barotrauma.Networking public void UpdateCheatsEnabled() { + if (!connectedClients.Any()) { return; } + var msg = server.CreateMessage(); msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED); msg.Write(DebugConsole.CheatsEnabled); msg.WritePadBits(); CompressOutgoingMessage(msg); - + server.SendMessage(msg, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0); } @@ -2794,747 +2794,6 @@ namespace Barotrauma.Networking server.Shutdown(DisconnectReason.ServerShutdown.ToString()); } - public void SendConsoleMessage(string txt, Client recipient) - { - ChatMessage msg = ChatMessage.Create("", txt, ChatMessageType.Console, null); - SendDirectChatMessage(msg, recipient); - } - - public void SendDirectChatMessage(ChatMessage msg, Client recipient) - { - if (recipient == null) - { - string errorMsg = "Attempted to send a chat message to a null client.\n" + Environment.StackTrace; - DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("GameServer.SendDirectChatMessage:ClientNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); - return; - } - - msg.NetStateID = recipient.ChatMsgQueue.Count > 0 ? - (ushort)(recipient.ChatMsgQueue.Last().NetStateID + 1) : - (ushort)(recipient.LastRecvChatMsgID + 1); - - recipient.ChatMsgQueue.Add(msg); - recipient.LastChatMsgQueueID = msg.NetStateID; - } - - /// - /// Add the message to the chatbox and pass it to all clients who can receive it - /// - public void SendChatMessage(string message, ChatMessageType? type = null, Client senderClient = null, Character senderCharacter = null) - { - string senderName = ""; - - Client targetClient = null; - - if (type == null) - { - string command = ChatMessage.GetChatMessageCommand(message, out string tempStr); - switch (command.ToLowerInvariant()) - { - case "r": - case "radio": - type = ChatMessageType.Radio; - break; - case "d": - case "dead": - type = ChatMessageType.Dead; - break; - default: - if (command != "") - { - if (command == name.ToLowerInvariant()) - { - //a private message to the host - } - else - { - targetClient = connectedClients.Find(c => - command == c.Name.ToLowerInvariant() || - (c.Character != null && command == c.Character.Name.ToLowerInvariant())); - - if (targetClient == null) - { - if (senderClient != null) - { - var chatMsg = ChatMessage.Create( - "", $"ServerMessage.PlayerNotFound~[player]={command}", - ChatMessageType.Error, null); - - chatMsg.NetStateID = senderClient.ChatMsgQueue.Count > 0 ? - (ushort)(senderClient.ChatMsgQueue.Last().NetStateID + 1) : - (ushort)(senderClient.LastRecvChatMsgID + 1); - - senderClient.ChatMsgQueue.Add(chatMsg); - senderClient.LastChatMsgQueueID = chatMsg.NetStateID; - } - else - { - AddChatMessage($"ServerMessage.PlayerNotFound~[player]={command}", ChatMessageType.Error); - } - - return; - } - } - - type = ChatMessageType.Private; - } - else - { - type = ChatMessageType.Default; - } - break; - } - - message = tempStr; - } - - if (gameStarted) - { - if (senderClient == null) - { - //msg sent by the server - if (senderCharacter == null) - { - senderName = name; - } - else //msg sent by an AI character - { - senderName = senderCharacter.Name; - } - } - else //msg sent by a client - { - senderCharacter = senderClient.Character; - senderName = senderCharacter == null ? senderClient.Name : senderCharacter.Name; - - //sender doesn't have a character or the character can't speak -> only ChatMessageType.Dead allowed - if (senderCharacter == null || senderCharacter.IsDead || senderCharacter.SpeechImpediment >= 100.0f) - { - type = ChatMessageType.Dead; - } - else if (type == ChatMessageType.Private) - { - //sender has an alive character, sending private messages not allowed - return; - } - - } - } - else - { - if (senderClient == null) - { - //msg sent by the server - if (senderCharacter == null) - { - senderName = name; - } - else //sent by an AI character, not allowed when the game is not running - { - return; - } - } - else //msg sent by a client - { - //game not started -> clients can only send normal and private chatmessages - if (type != ChatMessageType.Private) type = ChatMessageType.Default; - senderName = senderClient.Name; - } - } - - //check if the client is allowed to send the message - WifiComponent senderRadio = null; - switch (type) - { - case ChatMessageType.Radio: - case ChatMessageType.Order: - if (senderCharacter == null) return; - - //return if senderCharacter doesn't have a working radio - var radio = senderCharacter.Inventory?.Items.FirstOrDefault(i => i != null && i.GetComponent() != null); - if (radio == null || !senderCharacter.HasEquippedItem(radio)) return; - - senderRadio = radio.GetComponent(); - if (!senderRadio.CanTransmit()) return; - break; - case ChatMessageType.Dead: - //character still alive and capable of speaking -> dead chat not allowed - if (senderClient != null && senderCharacter != null && !senderCharacter.IsDead && senderCharacter.SpeechImpediment < 100.0f) - { - return; - } - break; - } - - if (type == ChatMessageType.Server) - { - senderName = null; - senderCharacter = null; - } - else if (type == ChatMessageType.Radio) - { - //send to chat-linked wifi components - senderRadio.TransmitSignal(0, message, senderRadio.Item, senderCharacter, false); - } - - //check which clients can receive the message and apply distance effects - foreach (Client client in ConnectedClients) - { - string modifiedMessage = message; - - switch (type) - { - case ChatMessageType.Default: - case ChatMessageType.Radio: - case ChatMessageType.Order: - if (senderCharacter != null && - client.Character != null && !client.Character.IsDead) - { - modifiedMessage = ChatMessage.ApplyDistanceEffect(message, (ChatMessageType)type, senderCharacter, client.Character); - - //too far to hear the msg -> don't send - if (string.IsNullOrWhiteSpace(modifiedMessage)) continue; - } - break; - case ChatMessageType.Dead: - //character still alive -> don't send - if (client != senderClient && client.Character != null && !client.Character.IsDead) continue; - break; - case ChatMessageType.Private: - //private msg sent to someone else than this client -> don't send - if (client != targetClient && client != senderClient) continue; - break; - } - - var chatMsg = ChatMessage.Create( - senderName, - modifiedMessage, - (ChatMessageType)type, - senderCharacter); - - SendDirectChatMessage(chatMsg, client); - } - - if (type.Value != ChatMessageType.MessageBox) - { - string myReceivedMessage = type == ChatMessageType.Server || type == ChatMessageType.Error ? TextManager.GetServerMessage(message) : message; - if (!string.IsNullOrWhiteSpace(myReceivedMessage) && - (targetClient == null || senderClient == null)) - { - AddChatMessage(myReceivedMessage, (ChatMessageType)type, senderName, senderCharacter); - } - } - } - - public void SendOrderChatMessage(OrderChatMessage message) - { - if (message.Sender == null || message.Sender.SpeechImpediment >= 100.0f) return; - ChatMessageType messageType = ChatMessage.CanUseRadio(message.Sender) ? ChatMessageType.Radio : ChatMessageType.Default; - - //check which clients can receive the message and apply distance effects - foreach (Client client in ConnectedClients) - { - string modifiedMessage = message.Text; - - if (message.Sender != null && - client.Character != null && !client.Character.IsDead) - { - modifiedMessage = ChatMessage.ApplyDistanceEffect(message.Text, messageType, message.Sender, client.Character); - - //too far to hear the msg -> don't send - if (string.IsNullOrWhiteSpace(modifiedMessage)) continue; - } - - SendDirectChatMessage(message, client); - } - - string myReceivedMessage = message.Text; - - if (!string.IsNullOrWhiteSpace(myReceivedMessage)) - { - AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, myReceivedMessage, message.TargetEntity, message.TargetCharacter, message.Sender)); - } - } - - private void FileTransferChanged(FileSender.FileTransferOut transfer) - { - Client recipient = connectedClients.Find(c => c.Connection == transfer.Connection); - if (transfer.FileType == FileTransferType.CampaignSave && - (transfer.Status == FileTransferStatus.Sending || transfer.Status == FileTransferStatus.Finished) && - recipient.LastCampaignSaveSendTime != null) - { - recipient.LastCampaignSaveSendTime.Second = (float)NetTime.Now; - } - } - - public void SendCancelTransferMsg(FileSender.FileTransferOut transfer) - { - NetOutgoingMessage msg = server.CreateMessage(); - msg.Write((byte)ServerPacketHeader.FILE_TRANSFER); - msg.Write((byte)FileTransferMessageType.Cancel); - msg.Write((byte)transfer.SequenceChannel); - CompressOutgoingMessage(msg); - server.SendMessage(msg, transfer.Connection, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel); - } - - public void UpdateVoteStatus() - { - if (server.Connections.Count == 0 || connectedClients.Count == 0) return; - - Client.UpdateKickVotes(connectedClients); - - var clientsToKick = connectedClients.FindAll(c => - c.Connection != OwnerConnection && - c.KickVoteCount >= connectedClients.Count * serverSettings.KickVoteRequiredRatio); - foreach (Client c in clientsToKick) - { - SendChatMessage($"ServerMessage.KickedFromServer~[client]={c.Name}", ChatMessageType.Server, null); - KickClient(c, "ServerMessage.KickedByVote"); - BanClient(c, "ServerMessage.KickedByVoteAutoBan", duration: TimeSpan.FromSeconds(serverSettings.AutoBanTime)); - } - - GameMain.NetLobbyScreen.LastUpdateID++; - - SendVoteStatus(connectedClients); - - if (serverSettings.Voting.AllowEndVoting && EndVoteMax > 0 && - ((float)EndVoteCount / (float)EndVoteMax) >= serverSettings.EndVoteRequiredRatio) - { - Log("Ending round by votes (" + EndVoteCount + "/" + (EndVoteMax - EndVoteCount) + ")", ServerLog.MessageType.ServerMessage); - EndGame(); - } - } - - public void SendVoteStatus(List recipients) - { - NetOutgoingMessage msg = server.CreateMessage(); - msg.Write((byte)ServerPacketHeader.UPDATE_LOBBY); - msg.Write((byte)ServerNetObject.VOTE); - serverSettings.Voting.ServerWrite(msg); - msg.Write((byte)ServerNetObject.END_OF_MESSAGE); - - CompressOutgoingMessage(msg); - - server.SendMessage(msg, recipients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0); - } - - public void UpdateClientPermissions(Client client) - { - if (client.SteamID > 0) - { - serverSettings.ClientPermissions.RemoveAll(cp => cp.SteamID == client.SteamID); - if (client.Permissions != ClientPermissions.None) - { - serverSettings.ClientPermissions.Add(new ServerSettings.SavedClientPermission( - client.Name, - client.SteamID, - client.Permissions, - client.PermittedConsoleCommands)); - } - } - else - { - serverSettings.ClientPermissions.RemoveAll(cp => client.IPMatches(cp.IP)); - if (client.Permissions != ClientPermissions.None) - { - serverSettings.ClientPermissions.Add(new ServerSettings.SavedClientPermission( - client.Name, - client.Connection.RemoteEndPoint.Address, - client.Permissions, - client.PermittedConsoleCommands)); - } - } - - var msg = server.CreateMessage(); - msg.Write((byte)ServerPacketHeader.PERMISSIONS); - client.WritePermissions(msg); - CompressOutgoingMessage(msg); - - //send the message to the client whose permissions are being modified and the clients who are allowed to modify permissions - List recipients = new List() { client.Connection }; - foreach (Client otherClient in connectedClients) - { - if (otherClient.HasPermission(ClientPermissions.ManagePermissions) && !recipients.Contains(otherClient.Connection)) - { - recipients.Add(otherClient.Connection); - } - } - server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0); - - serverSettings.SaveClientPermissions(); - } - - public void GiveAchievement(Character character, string achievementIdentifier) - { - achievementIdentifier = achievementIdentifier.ToLowerInvariant(); - foreach (Client client in connectedClients) - { - if (client.Character == character) - { - GiveAchievement(client, achievementIdentifier); - return; - } - } - } - - public void GiveAchievement(Client client, string achievementIdentifier) - { - if (client.GivenAchievements.Contains(achievementIdentifier)) return; - client.GivenAchievements.Add(achievementIdentifier); - - var msg = server.CreateMessage(); - msg.Write((byte)ServerPacketHeader.ACHIEVEMENT); - msg.Write(achievementIdentifier); - - CompressOutgoingMessage(msg); - - server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered); - } - - public void UpdateCheatsEnabled() - { - var msg = server.CreateMessage(); - msg.Write((byte)ServerPacketHeader.CHEATS_ENABLED); - msg.Write(DebugConsole.CheatsEnabled); - msg.WritePadBits(); - - CompressOutgoingMessage(msg); - - server.SendMessage(msg, connectedClients.Select(c => c.Connection).ToList(), NetDeliveryMethod.ReliableUnordered, 0); - } - - public void SetClientCharacter(Client client, Character newCharacter) - { - if (client == null) return; - - //the client's previous character is no longer a remote player - if (client.Character != null) - { - client.Character.IsRemotePlayer = false; - client.Character.OwnerClientIP = null; - client.Character.OwnerClientName = null; - } - - if (newCharacter == null) - { - if (client.Character != null) //removing control of the current character - { - CreateEntityEvent(client.Character, new object[] { NetEntityEvent.Type.Control, null }); - client.Character = null; - } - } - else //taking control of a new character - { - newCharacter.ClientDisconnected = false; - newCharacter.KillDisconnectedTimer = 0.0f; - newCharacter.ResetNetState(); - if (client.Character != null) - { - newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID; - } - - newCharacter.OwnerClientIP = client.Connection.RemoteEndPoint.Address.ToString(); - newCharacter.OwnerClientName = client.Name; - newCharacter.IsRemotePlayer = true; - newCharacter.Enabled = true; - client.Character = newCharacter; - CreateEntityEvent(newCharacter, new object[] { NetEntityEvent.Type.Control, client }); - } - } - - private void UpdateCharacterInfo(NetIncomingMessage message, Client sender) - { - sender.SpectateOnly = message.ReadBoolean() && (serverSettings.AllowSpectating || sender.Connection == OwnerConnection); - if (sender.SpectateOnly) - { - return; - } - - Gender gender = Gender.Male; - Race race = Race.White; - int headSpriteId = 0; - try - { - gender = (Gender)message.ReadByte(); - race = (Race)message.ReadByte(); - headSpriteId = message.ReadByte(); - } - catch (Exception e) - { - //gender = Gender.Male; - //race = Race.White; - //headSpriteId = 0; - DebugConsole.Log("Received invalid characterinfo from \"" + sender.Name + "\"! { " + e.Message + " }"); - } - int hairIndex = message.ReadByte(); - int beardIndex = message.ReadByte(); - int moustacheIndex = message.ReadByte(); - int faceAttachmentIndex = message.ReadByte(); - - List jobPreferences = new List(); - int count = message.ReadByte(); - for (int i = 0; i < Math.Min(count, 3); i++) - { - string jobIdentifier = message.ReadString(); - - JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Identifier == jobIdentifier); - if (jobPrefab != null) jobPreferences.Add(jobPrefab); - } - - sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name); - sender.CharacterInfo.RecreateHead(headSpriteId, race, gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex); - - //if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor - Debug.Assert(sender.JobPreferences.Count > 0); - if (jobPreferences.Count > 0) - { - sender.JobPreferences = jobPreferences; - } - } - - public void AssignJobs(List unassigned) - { - unassigned = new List(unassigned); - - Dictionary assignedClientCount = new Dictionary(); - foreach (JobPrefab jp in JobPrefab.List) - { - assignedClientCount.Add(jp, 0); - } - - Character.TeamType teamID = Character.TeamType.None; - if (unassigned.Count > 0) { teamID = unassigned[0].TeamID; } - - //if we're playing a multiplayer campaign, check which clients already have a character and a job - //(characters are persistent in campaigns) - if (GameMain.GameSession.GameMode is MultiPlayerCampaign multiplayerCampaign) - { - var campaignAssigned = multiplayerCampaign.GetAssignedJobs(connectedClients); - //remove already assigned clients from unassigned - unassigned.RemoveAll(u => campaignAssigned.ContainsKey(u)); - //add up to assigned client count - foreach (KeyValuePair clientJob in campaignAssigned) - { - assignedClientCount[clientJob.Value.Prefab]++; - clientJob.Key.AssignedJob = clientJob.Value.Prefab; - } - } - - //count the clients who already have characters with an assigned job - foreach (Client c in connectedClients) - { - if (c.TeamID != teamID || unassigned.Contains(c)) continue; - if (c.Character?.Info?.Job != null && !c.Character.IsDead) - { - assignedClientCount[c.Character.Info.Job.Prefab]++; - } - } - - //if any of the players has chosen a job that is Always Allowed, give them that job - for (int i = unassigned.Count - 1; i >= 0; i--) - { - if (unassigned[i].JobPreferences.Count == 0) continue; - if (!unassigned[i].JobPreferences[0].AllowAlways) continue; - unassigned[i].AssignedJob = unassigned[i].JobPreferences[0]; - unassigned.RemoveAt(i); - } - - //go throught the jobs whose MinNumber>0 (i.e. at least one crew member has to have the job) - bool unassignedJobsFound = true; - while (unassignedJobsFound && unassigned.Count > 0) - { - unassignedJobsFound = false; - - foreach (JobPrefab jobPrefab in JobPrefab.List) - { - if (unassigned.Count == 0) break; - if (jobPrefab.MinNumber < 1 || assignedClientCount[jobPrefab] >= jobPrefab.MinNumber) continue; - - //find the client that wants the job the most, or force it to random client if none of them want it - Client assignedClient = FindClientWithJobPreference(unassigned, jobPrefab, true); - - assignedClient.AssignedJob = jobPrefab; - assignedClientCount[jobPrefab]++; - unassigned.Remove(assignedClient); - - //the job still needs more crew members, set unassignedJobsFound to true to keep the while loop running - if (assignedClientCount[jobPrefab] < jobPrefab.MinNumber) unassignedJobsFound = true; - } - } - - //attempt to give the clients a job they have in their job preferences - for (int i = unassigned.Count - 1; i >= 0; i--) - { - foreach (JobPrefab preferredJob in unassigned[i].JobPreferences) - { - //the maximum number of players that can have this job hasn't been reached yet - // -> assign it to the client - if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && unassigned[i].Karma >= preferredJob.MinKarma) - { - unassigned[i].AssignedJob = preferredJob; - assignedClientCount[preferredJob]++; - unassigned.RemoveAt(i); - break; - } - } - } - - //give random jobs to rest of the clients - foreach (Client c in unassigned) - { - //find all jobs that are still available - var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma); - - //all jobs taken, give a random job - if (remainingJobs.Count == 0) - { - DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job..."); - int jobIndex = Rand.Range(0, JobPrefab.List.Count); - int skips = 0; - while (c.Karma < JobPrefab.List[jobIndex].MinKarma) - { - jobIndex++; - skips++; - if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count; - if (skips >= JobPrefab.List.Count) break; - } - c.AssignedJob = JobPrefab.List[jobIndex]; - assignedClientCount[c.AssignedJob]++; - } - else //some jobs still left, choose one of them by random - { - c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)]; - assignedClientCount[c.AssignedJob]++; - } - } - } - - public void AssignBotJobs(List bots, Character.TeamType teamID) - { - Dictionary assignedPlayerCount = new Dictionary(); - foreach (JobPrefab jp in JobPrefab.List) - { - assignedPlayerCount.Add(jp, 0); - } - - //count the clients who already have characters with an assigned job - foreach (Client c in connectedClients) - { - if (c.TeamID != teamID) continue; - if (c.Character?.Info?.Job != null && !c.Character.IsDead) - { - assignedPlayerCount[c.Character.Info.Job.Prefab]++; - } - else if (c.CharacterInfo?.Job != null) - { - assignedPlayerCount[c.CharacterInfo?.Job.Prefab]++; - } - } - - List unassignedBots = new List(bots); - foreach (CharacterInfo bot in bots) - { - foreach (JobPrefab jobPrefab in JobPrefab.List) - { - if (jobPrefab.MinNumber < 1 || assignedPlayerCount[jobPrefab] >= jobPrefab.MinNumber) continue; - bot.Job = new Job(jobPrefab); - assignedPlayerCount[jobPrefab]++; - unassignedBots.Remove(bot); - break; - } - } - - //find a suitable job for the rest of the players - foreach (CharacterInfo c in unassignedBots) - { - //find all jobs that are still available - var remainingJobs = JobPrefab.List.FindAll(jp => assignedPlayerCount[jp] < jp.MaxNumber); - //all jobs taken, give a random job - if (remainingJobs.Count == 0) - { - DebugConsole.ThrowError("Failed to assign a suitable job for bot \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job..."); - c.Job = new Job(JobPrefab.List[Rand.Range(0, JobPrefab.List.Count)]); - assignedPlayerCount[c.Job.Prefab]++; - } - else //some jobs still left, choose one of them by random - { - c.Job = new Job(remainingJobs[Rand.Range(0, remainingJobs.Count)]); - assignedPlayerCount[c.Job.Prefab]++; - } - } - } - - private Client FindClientWithJobPreference(List clients, JobPrefab job, bool forceAssign = false) - { - int bestPreference = 0; - Client preferredClient = null; - foreach (Client c in clients) - { - if (c.Karma < job.MinKarma) continue; - int index = c.JobPreferences.IndexOf(job); - if (index == -1) index = 1000; - - if (preferredClient == null || index < bestPreference) - { - bestPreference = index; - preferredClient = c; - } - } - - //none of the clients wants the job, assign it to random client - if (forceAssign && preferredClient == null) - { - preferredClient = clients[Rand.Int(clients.Count)]; - } - - return preferredClient; - } - - public static void Log(string line, ServerLog.MessageType messageType) - { - if (GameMain.Server == null || !GameMain.Server.ServerSettings.SaveServerLogs) return; - - GameMain.Server.ServerSettings.ServerLog.WriteLine(line, messageType); - - foreach (Client client in GameMain.Server.ConnectedClients) - { - if (!client.HasPermission(ClientPermissions.ServerLog)) continue; - //use sendername as the message type - GameMain.Server.SendDirectChatMessage( - ChatMessage.Create(messageType.ToString(), line, ChatMessageType.ServerLog, null), - client); - } - } - - public override void Disconnect() - { - serverSettings.BanList.Save(); - serverSettings.SaveSettings(); - SteamManager.CloseServer(); - - if (registeredToMaster) - { - if (restClient != null) - { - var request = new RestRequest("masterserver2.php", Method.GET); - request.AddParameter("action", "removeserver"); - request.AddParameter("serverport", Port); - restClient.Execute(request); - restClient = null; - } - } - - if (serverSettings.SaveServerLogs) - { - Log("Shutting down the server...", ServerLog.MessageType.ServerMessage); - serverSettings.ServerLog.Save(); - } - - GameAnalyticsManager.AddDesignEvent("GameServer:ShutDown"); - server.Shutdown("The server has been shut down"); - } - void InitUPnP() { server.UPnP.ForwardPort(NetPeerConfiguration.Port, "barotrauma"); diff --git a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index 5b91f148a..08a5f7a2e 100644 --- a/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Barotrauma/BarotraumaServer/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -222,7 +222,7 @@ namespace Barotrauma.Networking + (c.LastRecvEntityEventID + 1).ToString() + " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" + " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.ServerMessage); - server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesync"); + server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent"); } ); } @@ -236,7 +236,7 @@ namespace Barotrauma.Networking { DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red); GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.ServerMessage); - server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesync"); + server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent"); }); } } @@ -260,6 +260,7 @@ namespace Barotrauma.Networking //a client could potentially spam events with a much higher character state ID //than the state of their character and/or stop sending character inputs, //so we'll drop some events to make sure no-one blows up our buffer + DebugConsole.Log("Excessive amount of events in a client's event buffer. The client may be spamming events or their event IDs might be out of sync. Dropping events..."); bufferedEvents.RemoveRange(0, 256); } @@ -494,7 +495,6 @@ namespace Barotrauma.Networking foreach (Client c in server.ConnectedClients) { - c.PositionUpdateLastSent.Clear(); c.EntityEventLastSent.Clear(); c.LastRecvEntityEventID = 0; c.LastSentEntityEventID = 0; diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index d2f9399ee..2a1a400b3 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -73,8 +73,8 @@ + - diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index bec8045ae..0a90b8248 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -1005,9 +1005,36 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -1382,9 +1409,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -1619,6 +1643,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -1852,6 +1882,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -1873,6 +1906,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -3089,6 +3125,9 @@ PreserveNewest + + PreserveNewest + diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 7c7164b11..d4dc9e8b0 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -359,7 +359,7 @@ namespace Barotrauma if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(Character.CharacterHealth.CrushDepth * 0.75f)) { //steer straight up if very deep - steeringManager.SteeringManual(deltaTime, Vector2.UnitY * speed); + steeringManager.SteeringManual(deltaTime, Vector2.UnitY); return; } @@ -652,7 +652,7 @@ namespace Barotrauma { //wander around randomly and decrease the priority faster if no path is found if (selectedTargetMemory != null) selectedTargetMemory.Priority -= deltaTime * 10.0f; - steeringManager.SteeringWander(speed); + steeringManager.SteeringWander(); } else if (indoorsSteering.CurrentPath.Finished) { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index 898af1d9a..a232f65d1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -131,23 +131,20 @@ namespace Barotrauma ignorePlatforms = true; } } + + if (Character.IsClimbing && PathSteering.InLadders && PathSteering.IsNextLadderSameAsCurrent) + { + Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); + } } Character.AnimController.IgnorePlatforms = ignorePlatforms; - // Suspect that this causes issues when trying to exit from the ladders -> could try to check if the next node is ladder? - //if (Character.IsClimbing) - //{ - // Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); - //} - Vector2 targetMovement = AnimController.TargetMovement; if (!Character.AnimController.InWater) { - targetMovement = new Vector2( - Character.AnimController.TargetMovement.X, - MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)); + targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)); } float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed); @@ -227,10 +224,6 @@ namespace Barotrauma PropagateHullSafety(Character, Character.CurrentHull); } } - - private void ReportProblems() - { - if (GameMain.Client != null) return; protected void ReportProblems() { @@ -252,7 +245,7 @@ namespace Barotrauma foreach (Character c in Character.CharacterList) { if (c.CurrentHull == Character.CurrentHull && !c.IsDead && - (c.AIController is EnemyAIController || c.TeamID != Character.TeamID)) + (c.AIController is EnemyAIController || (c.TeamID != Character.TeamID && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC))) { var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders"); newOrder = new Order(orderPrefab, Character.CurrentHull, null); @@ -297,10 +290,13 @@ namespace Barotrauma public override void OnAttacked(Character attacker, AttackResult attackResult) { float damage = attackResult.Damage; - if (damage < 0) { return; } + if (damage <= 0) { return; } if (attacker == null || attacker.IsDead || attacker.Removed) { - objectiveManager.GetObjective().Priority = 100; + if (objectiveManager.CurrentOrder == null) + { + objectiveManager.GetObjective().Priority = 100; + } return; } if (IsFriendly(attacker)) @@ -314,7 +310,10 @@ namespace Barotrauma if (!attacker.IsRemotePlayer && Character.Controlled != attacker && attacker.AIController != null && attacker.AIController.Enabled) { // Don't react to damage done by friendly ai, because we know that it's accidental - objectiveManager.GetObjective().Priority = 100; + if (objectiveManager.CurrentOrder == null) + { + objectiveManager.GetObjective().Priority = 100; + } return; } float currentVitality = Character.CharacterHealth.Vitality; @@ -322,8 +321,10 @@ namespace Barotrauma if (dmgPercentage < currentVitality / 10) { // Don't react to a minor amount of (accidental) dmg done by friendly characters - objectiveManager.GetObjective().Priority = 100; - return; + if (objectiveManager.CurrentOrder == null) + { + objectiveManager.GetObjective().Priority = 100; + } } if (ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective) { @@ -465,7 +466,9 @@ namespace Barotrauma // Even the smallest fire reduces the safety by 50% float fire = hull.FireSources.Count * 0.5f + hull.FireSources.Sum(fs => fs.DamageRange) / hull.Size.X; float fireFactor = ignoreFire ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(fire, 0, 1)); - int enemyCount = Character.CharacterList.Count(e => e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious && (e.AIController is EnemyAIController || e.TeamID != character.TeamID)); + int enemyCount = Character.CharacterList.Count(e => + e.CurrentHull == hull && !e.IsDead && !e.IsUnconscious && + (e.AIController is EnemyAIController || (e.TeamID != character.TeamID && character.TeamID != Character.TeamType.FriendlyNPC && e.TeamID != Character.TeamType.FriendlyNPC))); // The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages) float enemyFactor = ignoreEnemies ? 1 : MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1)); float safety = oxygenFactor * waterFactor * fireFactor * enemyFactor; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs index 723db7700..532d715f3 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/IndoorsSteeringManager.cs @@ -44,8 +44,8 @@ namespace Barotrauma } public bool InLadders => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Ladders != null; - private bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null; - private bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders; + public bool IsNextNodeLadder => currentPath.NextNode != null && currentPath.CurrentNode.Ladders != null; + public bool IsNextLadderSameAsCurrent => IsNextNodeLadder && currentPath.CurrentNode.Ladders == currentPath.NextNode.Ladders; public bool InStairs => currentPath != null && currentPath.CurrentNode != null && currentPath.CurrentNode.Stairs != null; @@ -213,7 +213,7 @@ namespace Barotrauma } else if (character.AnimController.InWater) { - if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < collider.radius * collider.radius) + if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2)) { currentPath.SkipToNextNode(); } @@ -221,11 +221,14 @@ namespace Barotrauma else { Vector2 colliderBottom = character.AnimController.GetColliderBottom(); + Vector2 colliderSize = collider.GetSize(); + // Cannot use the head position, because not all characters have head or it can be below the total height of the character + float characterHeight = colliderSize.Y + character.AnimController.ColliderHeightFromFloor; float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X); - bool isAboveVerticalPosition = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y; - bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + 1.5f; + bool isAboveFeet = currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y; + bool isNotTooHigh = currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + characterHeight; - if (horizontalDistance < collider.radius * 2 && isAboveVerticalPosition && isNotTooHigh) + if (horizontalDistance < collider.radius * 3 && isAboveFeet && isNotTooHigh) { currentPath.SkipToNextNode(); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/NPCConversation.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/NPCConversation.cs index ccc6363c6..934817d00 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/NPCConversation.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/NPCConversation.cs @@ -31,6 +31,7 @@ namespace Barotrauma List> contentPackageFiles = new List>(); foreach (string filePath in filePaths) { + if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported XDocument doc = XMLExtensions.TryLoadXml(filePath); if (doc == null || doc.Root == null) continue; string language = doc.Root.GetAttributeString("Language", "English"); @@ -41,6 +42,7 @@ namespace Barotrauma List> translationFiles = new List>(); foreach (string filePath in Directory.GetFiles(Path.Combine("Content", "NPCConversations"))) { + if (Path.GetExtension(filePath) == ".csv") continue; // .csv files are not supported XDocument doc = XMLExtensions.TryLoadXml(filePath); if (doc == null || doc.Root == null) continue; string language = doc.Root.GetAttributeString("Language", "English"); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs index a4ae023a2..ba4320194 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs @@ -139,6 +139,7 @@ namespace Barotrauma } protected virtual bool ShouldInterruptSubObjective(AIObjective subObjective) => false; + public virtual void OnSelected() { } protected abstract void Act(float deltaTime); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 1f8813858..8aac40d42 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -117,6 +117,17 @@ namespace Barotrauma return weapon; } + private void Unequip() + { + if (character.SelectedItems.Contains(Weapon)) + { + if (!Weapon.AllowedSlots.Contains(InvSlotType.Any) || !character.Inventory.TryPutItem(Weapon, character, new List() { InvSlotType.Any })) + { + Weapon.Drop(character); + } + } + } + private bool Equip(float deltaTime) { if (!character.SelectedItems.Contains(Weapon)) @@ -241,7 +252,19 @@ namespace Barotrauma HumanAIController.ObjectiveManager.GetObjective().Priority = 100; } - public override bool IsCompleted() => Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0.0f; + public override bool IsCompleted() + { + bool completed = Enemy == null || Enemy.Removed || Enemy.IsDead || coolDownTimer <= 0; + if (completed) + { + if (Weapon != null) + { + Unequip(); + } + } + return completed; + } + public override bool CanBeCompleted => !abandon && (reloadWeaponObjective == null || reloadWeaponObjective.CanBeCompleted) && (retreatObjective == null || retreatObjective.CanBeCompleted); public override float GetPriority(AIObjectiveManager objectiveManager) => Enemy == null || Enemy.Removed || Enemy.IsDead ? 0 : 100; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs index e85af365b..7fccbf418 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs @@ -18,7 +18,7 @@ namespace Barotrauma private bool isCompleted; - public bool IgnoreAlreadyContainedItems; + public string[] ignoredContainerIdentifiers; public Func GetItemPriority; @@ -95,7 +95,7 @@ namespace Barotrauma getItemObjective = new AIObjectiveGetItem(character, itemIdentifiers) { GetItemPriority = GetItemPriority, - IgnoreContainedItems = IgnoreAlreadyContainedItems + ignoredContainerIdentifiers = ignoredContainerIdentifiers }; AddSubObjective(getItemObjective); return; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFire.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFire.cs index ad375e55e..3c6c35ce4 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFire.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFire.cs @@ -78,16 +78,18 @@ namespace Barotrauma foreach (FireSource fs in targetHull.FireSources) { - // TODO: check if in the same room? bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range)); - if (!character.IsClimbing && (inRange || useExtinquisherTimer > 0.0f)) + if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f)) { useExtinquisherTimer += deltaTime; if (useExtinquisherTimer > 2.0f) useExtinquisherTimer = 0.0f; character.CursorPosition = fs.Position; character.SetInput(InputType.Aim, false, true); - character.AIController.SteeringManager.Reset(); + if (!character.IsClimbing) + { + character.AIController.SteeringManager.Reset(); + } extinguisher.Use(deltaTime, character); if (!targetHull.FireSources.Contains(fs)) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs index b2158a614..647275d2e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs @@ -1,5 +1,5 @@ using Barotrauma.Items.Components; -using System; +using Microsoft.Xna.Framework; using System.Linq; namespace Barotrauma @@ -10,31 +10,26 @@ namespace Barotrauma public override bool ForceRun => true; private AIObjective subObjective; - private string gearTag; public override bool IsCompleted() { for (int i = 0; i < character.Inventory.Items.Length; i++) { - if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) continue; + if (character.Inventory.SlotTypes[i] == InvSlotType.Any || character.Inventory.Items[i] == null) { continue; } if (character.Inventory.Items[i].HasTag(gearTag)) { var containedItems = character.Inventory.Items[i].ContainedItems; - if (containedItems == null) continue; + if (containedItems == null) { continue; } var oxygenTank = containedItems.FirstOrDefault(it => (it.Prefab.Identifier == "oxygentank" || it.HasTag("oxygensource")) && it.Condition > 0.0f); - if (oxygenTank != null) return true; + if (oxygenTank != null) { return true; } } } - return false; } - public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted; - - public AIObjectiveFindDivingGear(Character character, bool needDivingSuit) - : base(character, "") + public AIObjectiveFindDivingGear(Character character, bool needDivingSuit) : base(character, "") { gearTag = needDivingSuit ? "divingsuit" : "diving"; } @@ -54,12 +49,11 @@ namespace Barotrauma else { var containedItems = item.ContainedItems; - if (containedItems == null) return; - + if (containedItems == null) { return; } //check if there's an oxygen tank in the mask/suit foreach (Item containedItem in containedItems) { - if (containedItem == null) continue; + if (containedItem == null) { continue; } if (containedItem.Condition <= 0.0f) { containedItem.Drop(character); @@ -69,36 +63,21 @@ namespace Barotrauma //we've got an oxygen source inside the mask/suit, all good return; } - } - + } if (!(subObjective is AIObjectiveContainItem) || subObjective.IsCompleted()) { character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f); subObjective = new AIObjectiveContainItem(character, new string[] { "oxygentank", "oxygensource" }, item.GetComponent()); } } - if (subObjective != null) { subObjective.TryComplete(deltaTime); } } - public override float GetPriority(AIObjectiveManager objectiveManager) - { - if (character.AnimController.CurrentHull == null) return 100.0f; - - if (objectiveManager.CurrentOrder == this) - { - return AIObjectiveManager.OrderPriority; - } - - return 100.0f - character.Oxygen; - } - - public override bool IsDuplicate(AIObjective otherObjective) - { - return otherObjective is AIObjectiveFindDivingGear; - } + public override bool CanBeCompleted => subObjective == null || subObjective.CanBeCompleted; + public override float GetPriority(AIObjectiveManager objectiveManager) => MathHelper.Clamp(100 - character.OxygenAvailable, 0, 100); + public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFindDivingGear; } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 6ad1eeca8..f3afac468 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Barotrauma.Items.Components; namespace Barotrauma { @@ -25,7 +24,7 @@ namespace Barotrauma private float searchHullTimer; private AIObjectiveGoTo goToObjective; - private AIObjective divingGearObjective; + private AIObjectiveFindDivingGear divingGearObjective; public AIObjectiveFindSafety(Character character) : base(character, "") { } @@ -35,13 +34,12 @@ namespace Barotrauma protected override void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; - if (HumanAIController.NeedsDivingGear(currentHull)) + if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null) { bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90; bool hasEquipment = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character); - if ((divingGearObjective == null || !divingGearObjective.CanBeCompleted) && !hasEquipment) + if (!hasEquipment) { - // If the previous objective cannot be completed, create a new and try again. divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit); } } @@ -55,9 +53,15 @@ namespace Barotrauma } else if (divingGearObjective.CanBeCompleted) { - // If diving gear objective is active, wait for it to complete. + // If diving gear objective is active and can be completed, wait for it to complete. return; } + else + { + divingGearObjective = null; + // Reset the timer so that we get a safe hull target. + searchHullTimer = 0; + } } if (unreachableClearTimer > 0) @@ -83,17 +87,18 @@ namespace Barotrauma { if (goToObjective.Target != bestHull) { - goToObjective = new AIObjectiveGoTo(bestHull, character) + // If we need diving gear, we should already have it, if possible. + goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false) { - AllowGoingOutside = true + AllowGoingOutside = HumanAIController.HasDivingSuit(character) }; } } else { - goToObjective = new AIObjectiveGoTo(bestHull, character) + goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false) { - AllowGoingOutside = true + AllowGoingOutside = HumanAIController.HasDivingSuit(character) }; } } @@ -110,6 +115,7 @@ namespace Barotrauma unreachable.Add(goToObjective.Target as Hull); } goToObjective = null; + SteeringManager.SteeringWander(); } } else if (currentHull != null) @@ -126,6 +132,11 @@ namespace Barotrauma foreach (Character enemy in Character.CharacterList) { + //don't run from friendly NPCs + if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; } + //friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters) + if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; } + if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious && (enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID)) { @@ -170,13 +181,15 @@ namespace Barotrauma if (unreachable.Contains(hull)) { continue; } if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; } hullSafety = HumanAIController.GetHullSafety(hull, character); - var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); - int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull)); // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist)); hullSafety *= distanceFactor; // Each unsafe node reduces the hull safety value. + // Ignore current hull, because otherwise the would block all paths from the current hull to the target hull. + var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); + if (path.Unreachable) { continue; } + int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull)); hullSafety /= 1 + unsafeNodes; // If the target is not inside a friendly submarine, considerably reduce the hull safety. if (!character.Submarine.IsEntityFoundOnThisSub(hull, true)) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs index 8a29fefcd..d575a62e4 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs @@ -31,7 +31,7 @@ namespace Barotrauma public override bool IsCompleted() { - return leak.Open <= 0.0f || leak.Removed || pathUnreachable; + return leak.Open <= 0.0f || leak.Removed; } public override bool CanBeCompleted => !abandon && base.CanBeCompleted; @@ -94,16 +94,15 @@ namespace Barotrauma if (repairTool == null) { return; } Vector2 gapDiff = leak.WorldPosition - character.WorldPosition; - var humanoidController = character.AnimController as HumanoidAnimController; // TODO: use the collider size/reach? - if (!character.AnimController.InWater && humanoidController != null && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150) + if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150) { - ((HumanoidAnimController)character.AnimController).Crouching = true; + HumanAIController.AnimController.Crouching = true; } - float armLength = humanoidController != null ? ConvertUnits.ToDisplayUnits(humanoidController.ArmLength) : 100; - bool cannotReach = gapDiff.Length() > armLength + repairTool.Range; + float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range); + bool cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach; if (cannotReach) { if (gotoObjective != null) @@ -116,7 +115,10 @@ namespace Barotrauma } else { - gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character); + gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character) + { + CloseEnough = reach * 0.75f + }; if (!subObjectives.Contains(gotoObjective)) { AddSubObjective(gotoObjective); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs index 68428100b..7f20924c1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; +using Barotrauma.Extensions; namespace Barotrauma { @@ -14,19 +15,15 @@ namespace Barotrauma //can be either tags or identifiers private string[] itemIdentifiers; - private Item targetItem, moveToTarget; - private int currSearchIndex; - - public bool IgnoreContainedItems; - + public string[] ignoredContainerIdentifiers; private AIObjectiveGoTo goToObjective; - private float currItemPriority; - private bool equip; + private HashSet ignoredItems = new HashSet(); + private bool canBeCompleted = true; public override bool CanBeCompleted => canBeCompleted; @@ -36,25 +33,19 @@ namespace Barotrauma { return AIObjectiveManager.OrderPriority; } - return 1.0f; } - public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) - : base(character, "") + public AIObjectiveGetItem(Character character, Item targetItem, bool equip = false) : base(character, "") { currSearchIndex = -1; this.equip = equip; this.targetItem = targetItem; } - public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false) - : this(character, new string[] { itemIdentifier }, equip) - { - } + public AIObjectiveGetItem(Character character, string itemIdentifier, bool equip = false) : this(character, new string[] { itemIdentifier }, equip) { } - public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false) - : base(character, "") + public AIObjectiveGetItem(Character character, string[] itemIdentifiers, bool equip = false) : base(character, "") { currSearchIndex = -1; this.equip = equip; @@ -108,12 +99,12 @@ namespace Barotrauma FindTargetItem(); if (targetItem == null || moveToTarget == null) { - // TODO: cannot be completed? - character?.AIController?.SteeringManager?.Reset(); + SteeringManager.SteeringWander(); return; } - if (Vector2.Distance(character.Position, moveToTarget.Position) < targetItem.InteractDistance * 2.0f) + if (moveToTarget.CurrentHull == character.CurrentHull && + Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance * 2, 2)) { int targetSlot = -1; if (equip) @@ -169,9 +160,13 @@ namespace Barotrauma } goToObjective.TryComplete(deltaTime); - if (!goToObjective.CanBeCompleted) targetItem = null; + if (!goToObjective.CanBeCompleted) + { + targetItem = null; + moveToTarget = null; + ignoredItems.Add(targetItem); + } } - } /// @@ -196,22 +191,30 @@ namespace Barotrauma currSearchIndex++; var item = Item.ItemList[currSearchIndex]; + if (ignoredItems.Contains(item)) { continue; } + if (item.Submarine == null) { continue; } + else if (item.Submarine.TeamID != character.TeamID) { continue; } + else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { continue; } - if (item.CurrentHull == null || item.Condition <= 0.0f) continue; - if (IgnoreContainedItems && item.Container != null) continue; - if (!itemIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) continue; + if (item.CurrentHull == null || item.Condition <= 0.0f) { continue; } + if (itemIdentifiers.None(id => item.Prefab.Identifier == id || item.HasTag(id))) { continue; } + + if (ignoredContainerIdentifiers != null && item.Container != null) + { + if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; } + } //if the item is inside a character's inventory, don't steal it unless the character is dead if (item.ParentInventory is CharacterInventory) { - if (item.ParentInventory.Owner is Character owner && !owner.IsDead) continue; + if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; } } //if the item is inside an item, which is inside a character's inventory, don't steal it Item rootContainer = item.GetRootContainer(); if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory) { - if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) continue; + if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; } } float itemPriority = 0.0f; @@ -219,13 +222,13 @@ namespace Barotrauma { //ignore if the item has zero priority itemPriority = GetItemPriority(item); - if (itemPriority <= 0.0f) continue; + if (itemPriority <= 0.0f) { continue; } } itemPriority = itemPriority - Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f; //ignore if the item has a lower priority than the currently selected one - if (moveToTarget != null && itemPriority < currItemPriority) continue; + if (moveToTarget != null && itemPriority < currItemPriority) { continue; } currItemPriority = itemPriority; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs index f4a2ab366..f96dd6153 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -186,11 +186,11 @@ namespace Barotrauma bool completed = false; - float allowedDistance = 0.5f; + float allowedDistance = CloseEnough; if (Target is Item item) { - allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), allowedDistance); + allowedDistance = Math.Max(ConvertUnits.ToSimUnits(item.InteractDistance), CloseEnough); if (item.IsInsideTrigger(character.WorldPosition)) completed = true; } else if (Target is Character targetCharacter) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index 7d0794a5a..a4138c65c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -200,6 +200,7 @@ namespace Barotrauma // Check that there is no unsafe or forbidden hulls on the way to the target // Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull. var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); + if (path.Unreachable) { continue; } if (path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs index 070d8c2c8..0be3827fd 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs @@ -30,9 +30,7 @@ namespace Barotrauma base.Update(objectiveManager, deltaTime); if (ignoreListTimer > ignoreListClearInterval) { - ignoreList.Clear(); - ignoreListTimer = 0; - UpdateTargets(); + Reset(); } else { @@ -67,16 +65,24 @@ namespace Barotrauma } } + private void Reset() + { + ignoreList.Clear(); + ignoreListTimer = 0; + UpdateTargets(); + } + + public override void OnSelected() + { + Reset(); + } + public override float GetPriority(AIObjectiveManager objectiveManager) { if (character.Submarine == null) { return 0; } if (targets.None()) { return 0; } float avg = targets.Average(t => Average(t)); - if (objectiveManager.CurrentOrder == this) - { - return AIObjectiveManager.OrderPriority - MathHelper.Max(0, AIObjectiveManager.OrderPriority - avg); - } - return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority, avg / 100); + return MathHelper.Lerp(0, AIObjectiveManager.OrderPriority + 20, avg / 100); } protected void UpdateTargets() diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs index 31b2a9015..988518098 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveManager.cs @@ -67,6 +67,7 @@ namespace Barotrauma private AIObjective GetCurrentObjective() { + var previousObjective = CurrentObjective; var firstObjective = Objectives.FirstOrDefault(); if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority(this) > firstObjective.GetPriority(this)) { @@ -76,6 +77,10 @@ namespace Barotrauma { CurrentObjective = firstObjective; } + if (previousObjective != CurrentObjective) + { + CurrentObjective?.OnSelected(); + } return CurrentObjective; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs index fe0730eff..99a856cd5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using System; using System.Linq; using Barotrauma.Extensions; +using FarseerPhysics; namespace Barotrauma { @@ -84,7 +85,14 @@ namespace Barotrauma } if (character.CanInteractWith(Item)) { - OperateRepairTool(deltaTime); + if (repairTool == null) + { + FindRepairTool(); + } + if (repairTool != null) + { + OperateRepairTool(deltaTime); + } foreach (Repairable repairable in Item.Repairables) { if (repairable.CurrentFixer != null && repairable.CurrentFixer != character) @@ -121,13 +129,17 @@ namespace Barotrauma subObjectives.Remove(goToObjective); } goToObjective = new AIObjectiveGoTo(Item, character); + if (repairTool != null) + { + goToObjective.CloseEnough = (HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range)) * 0.75f; + } AddSubObjective(goToObjective); } } - private void OperateRepairTool(float deltaTime) + private RepairTool repairTool; + private void FindRepairTool() { - // Operate repair tool, if required. foreach (Repairable repairable in Item.Repairables) { foreach (var kvp in repairable.requiredItems) @@ -138,19 +150,29 @@ namespace Barotrauma { if (requiredItem.MatchesItem(item)) { - var repairTool = item.GetComponent(); - if (repairTool != null) - { - character.CursorPosition = Item.Position; - character.SetInput(InputType.Aim, false, true); - repairTool.Use(deltaTime, character); - return; - } + repairTool = item.GetComponent(); } } } } } } + + private void OperateRepairTool(float deltaTime) + { + character.CursorPosition = Item.Position; + character.SetInput(InputType.Aim, false, true); + Vector2 fromToolToTarget = Item.Position - repairTool.Item.Position; + if (fromToolToTarget.LengthSquared() < MathUtils.Pow(repairTool.Range / 2, 2)) + { + // Too close -> steer away + character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - Item.SimPosition) / 2); + } + if (character.IsClimbing || + VectorExtensions.Angle(VectorExtensions.Forward(repairTool.Item.body.TransformedRotation), fromToolToTarget) < MathHelper.PiOver4) + { + repairTool.Use(deltaTime, character); + } + } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs index 4433311ba..2b2afd3f5 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs @@ -51,8 +51,14 @@ namespace Barotrauma { foreach (Repairable repairable in item.Repairables) { - if (item.Condition > repairable.ShowRepairUIThreshold) { ignore = true; } - else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character)) { ignore = true; } + if (!objectives.ContainsKey(item) && item.Condition > repairable.ShowRepairUIThreshold) + { + ignore = true; + } + else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character)) + { + ignore = true; + } if (ignore) { break; } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs index a6b21cd00..df3e8eb31 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs @@ -203,7 +203,7 @@ namespace Barotrauma { DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed); - return new SteeringPath(); + return new SteeringPath(true); } closestDist = 0.0f; @@ -225,8 +225,12 @@ namespace Barotrauma //if searching for a path inside the sub, make sure the waypoint is visible if (insideSubmarine) { + // TODO: for some reason fails to find the path when the sub is flooding. Disabling this check helps fixes it, but we can't disable it var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition); - if (body != null && body.UserData is Structure) continue; + if (body != null && body.UserData is Structure) + { + continue; + } } closestDist = dist; @@ -237,10 +241,9 @@ namespace Barotrauma if (endNode == null) { DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed); - return new SteeringPath(); + return new SteeringPath(true); } - var path = FindPath(startNode, endNode); return path; @@ -266,7 +269,7 @@ namespace Barotrauma if (startNode == null || endNode == null) { DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed); - return new SteeringPath(); + return new SteeringPath(true); } return FindPath(startNode, endNode); @@ -290,13 +293,12 @@ namespace Barotrauma node.G = 0.0f; node.H = 0.0f; } - + start.state = 1; while (true) { - PathNode currNode = null; - float dist = 10000.0f; + float dist = float.MaxValue; foreach (PathNode node in nodes) { if (node.state != 1) continue; @@ -364,7 +366,7 @@ namespace Barotrauma if (end.state == 0 || end.Parent == null) { - //path not found + //DebugConsole.NewMessage("Pathfinding error: path not found", Color.DarkRed); return new SteeringPath(true); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs index 7ec344357..ce2078c17 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs @@ -683,12 +683,6 @@ namespace Barotrauma limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength); } - while (referenceLimb.Rotation - angle < -MathHelper.TwoPi) - { - angle -= MathHelper.TwoPi; - } - - limb?.body.SmoothRotate(angle, torque, wrapAngle: false); } private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index 57e1501f6..241a2fb3b 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -1059,6 +1059,8 @@ namespace Barotrauma } } + private float prevFootPos; + void UpdateClimbing() { if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent() == null) @@ -1137,15 +1139,34 @@ namespace Barotrauma handPos.X - Dir * 0.05f, bottomPos + ColliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y); - MoveLimb(leftFoot, - new Vector2(footPos.X, - (slide ? footPos.Y : MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight) + ladderSimPos.Y), - 15.5f, true); + if (slide) + { + MoveLimb(leftFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true); + MoveLimb(rightFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true); + } + else + { + float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight; + float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight; + MoveLimb(leftFoot, new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), 15.5f, true); - MoveLimb(rightFoot, - new Vector2(footPos.X, - (slide ? footPos.Y : MathUtils.Round(footPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y), - 15.5f, true); + float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f); + float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f); + MoveLimb(rightFoot, new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), 15.5f, true); +#if CLIENT + if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval) + { + SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition); + leftFoot.LastImpactSoundTime = (float)Timing.TotalTime; + } + if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval) + { + SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition); + rightFoot.LastImpactSoundTime = (float)Timing.TotalTime; + } +#endif + prevFootPos = footPos.Y; + } //apply torque to the legs to make the knees bend Limb leftLeg = GetLimb(LimbType.LeftLeg); @@ -1781,14 +1802,24 @@ namespace Barotrauma float c = Vector2.Distance(pos, shoulderPos); c = MathHelper.Clamp(c, Math.Abs(upperArmLength - forearmLength), forearmLength + upperArmLength - 0.01f); - float ang2 = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2; + float armAngle = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2; - float armAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c); - float handAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c); + float upperArmAngle = MathUtils.SolveTriangleSSS(forearmLength, upperArmLength, c) * Dir; + float lowerArmAngle = MathUtils.SolveTriangleSSS(upperArmLength, forearmLength, c) * Dir; + + //make sure the arm angle "has the same number of revolutions" as the arm + while (arm.Rotation - armAngle > MathHelper.Pi) + { + armAngle += MathHelper.TwoPi; + } + while (arm.Rotation - armAngle < -MathHelper.Pi) + { + armAngle -= MathHelper.TwoPi; + } - arm?.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f * force * arm.Mass); - forearm?.body.SmoothRotate((ang2 + handAngle * Dir), 20.0f * force * forearm.Mass); - hand?.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force * hand.Mass); + arm?.body.SmoothRotate((armAngle - upperArmAngle), 20.0f * force * arm.Mass, wrapAngle: false); + forearm?.body.SmoothRotate((armAngle + lowerArmAngle), 20.0f * force * forearm.Mass, wrapAngle: false); + hand?.body.SmoothRotate((armAngle + lowerArmAngle), 100.0f * force * hand.Mass, wrapAngle: false); } private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index b0333fdc5..db8b8cb03 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -86,7 +86,10 @@ namespace Barotrauma public bool onGround; private bool ignorePlatforms; - protected float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale; + /// + /// In sim units. Joint scale applied. + /// + public float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale; public Structure Stairs; @@ -1324,6 +1327,19 @@ namespace Barotrauma } if (errorMsg != null) { + if (character.IsRemotePlayer) + { + errorMsg += " Ragdoll controlled remotely."; + } + if (SimplePhysicsEnabled) + { + errorMsg += " Simple physics enabled."; + } + if (GameMain.NetworkMember != null) + { + errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; + } + #if DEBUG DebugConsole.ThrowError(errorMsg); #else @@ -1343,7 +1359,6 @@ namespace Barotrauma SetInitialLimbPositions(); return; } - UpdateProjSpecific(deltaTime); } partial void UpdateProjSpecific(float deltaTime); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 31d6c2941..02752edfe 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -120,8 +120,6 @@ namespace Barotrauma private List statusEffects = new List(); private List speedMultipliers = new List(); - private List statusEffects = new List(); - public Entity ViewTarget { get; @@ -330,7 +328,8 @@ namespace Barotrauma pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f); } } - + + private float ragdollingLockTimer; public bool IsRagdolled; public bool IsForceRagdolled; public bool dontFollowCursor; @@ -1877,8 +1876,6 @@ namespace Barotrauma } speechImpedimentSet = false; - - if (needsAir) { bool protectedFromPressure = PressureProtection > 0.0f; @@ -1935,9 +1932,23 @@ namespace Barotrauma //Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us! bool allowRagdoll = GameMain.NetworkMember != null ? GameMain.NetworkMember.ServerSettings.AllowRagdollButton : true; if (IsForceRagdolled) + { IsRagdolled = IsForceRagdolled; - else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f)) //Keep us ragdolled if we were forced or we're too speedy to unragdoll - IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves + } + //Keep us ragdolled if we were forced or we're too speedy to unragdoll + else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f)) + { + if (ragdollingLockTimer > 0.0f) + { + ragdollingLockTimer -= deltaTime; + } + else + { + bool wasRagdolled = IsRagdolled; + IsRagdolled = IsKeyDown(InputType.Ragdoll); //Handle this here instead of Control because we can stop being ragdolled ourselves + if (wasRagdolled != IsRagdolled) { ragdollingLockTimer = 0.25f; } + } + } UpdateSightRange(); UpdateSoundRange(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/NPCPersonalityTrait.cs b/Barotrauma/BarotraumaShared/Source/Characters/NPCPersonalityTrait.cs index d4754c5c5..d19507a1e 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/NPCPersonalityTrait.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/NPCPersonalityTrait.cs @@ -19,6 +19,10 @@ namespace Barotrauma public readonly List AllowedDialogTags; private float commonness; + public float Commonness + { + get { return commonness; } + } public NPCPersonalityTrait(XElement element) { diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index 64803cc93..17347cacc 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -457,20 +457,6 @@ namespace Barotrauma } })); -#if CLIENT && WINDOWS - commands.Add(new Command("copyitemnames", "", (string[] args) => - { - StringBuilder sb = new StringBuilder(); - foreach (MapEntityPrefab mp in MapEntityPrefab.List) - { - if (!(mp is ItemPrefab)) continue; - sb.AppendLine(mp.Name); - } - System.Windows.Clipboard.SetText(sb.ToString()); - })); -#endif - - commands.Add(new Command("findentityids", "findentityids [entityname]", (string[] args) => { if (args.Length == 0) return; @@ -1435,58 +1421,25 @@ namespace Barotrauma #if SERVER if (GameMain.Server != null) { - case "cursor": - spawnPos = cursorPos; - break; - case "inventory": - spawnInventory = controlledCharacter?.Inventory; - break; - case "cargo": - var wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub); - spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; - break; - default: - //Check if last arg matches the name of an in-game player - if (GameMain.Server != null) - { - var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower()); - if (client == null) - { - NewMessage("No player found with the name \"" + args.Last() + "\". Spawning item at random location. If the player you want to give the item to has a space in their name, try surrounding their name with quotes (\").", Color.Red); - break; - } - else if (client.Character == null) - { - errorMsg = "The player \"" + args.Last() + "\" is connected, but hasn't spawned yet."; - return; - } - else - { - //If the last arg matches the name of an in-game player, set the destination to their inventory. - spawnInventory = client.Character.Inventory; - break; - } - } - else - { - var matchingCharacter = FindMatchingCharacter(args.Skip(1).ToArray()); - if (matchingCharacter?.Inventory != null) spawnInventory = matchingCharacter.Inventory; - } - break; + var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower()); + if (client != null) + { + extraParams += 1; + itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant(); + if (client.Character != null && client.Character.Name == args.Last().ToLower()) spawnInventory = client.Character.Inventory; + itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab; + } } #endif } - - string itemName = args[0]; - - var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab; + //Check again if the item can be found again after having checked for a character if (itemPrefab == null) { errorMsg = "Item \"" + itemName + "\" not found!"; return; } - if (spawnPos == null && spawnInventory == null) + if ((spawnPos == null || spawnPos == Vector2.Zero) && spawnInventory == null) { var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; @@ -1495,6 +1448,7 @@ namespace Barotrauma if (spawnPos != null) { Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); + } else if (spawnInventory != null) { diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs index 038b6a441..220e756cd 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs @@ -12,7 +12,7 @@ namespace Barotrauma public bool CheatsEnabled; - const int InitialMoney = 4500; + const int InitialMoney = 4700; protected bool watchmenSpawned; protected Character startWatchman, endWatchman; diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs index b961107de..461209f64 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/MultiPlayerCampaign.cs @@ -45,8 +45,6 @@ namespace Barotrauma private static byte currentCampaignID; - private List characterData = new List(); - public byte CampaignID { get; private set; @@ -217,7 +215,7 @@ namespace Barotrauma break; } } - +#if SERVER characterData.Clear(); string characterDataPath = GetCharacterDataSavePath(); var characterDataDoc = XMLExtensions.TryLoadXml(characterDataPath); @@ -226,33 +224,8 @@ namespace Barotrauma { characterData.Add(new CharacterCampaignData(subElement)); } +#endif } - public override void Save(XElement element) - { - XElement modeElement = new XElement("MultiPlayerCampaign", - new XAttribute("money", Money), - new XAttribute("cheatsenabled", CheatsEnabled)); - Map.Save(modeElement); - element.Add(modeElement); - - //save character data to a separate file - string characterDataPath = GetCharacterDataSavePath(); - XDocument characterDataDoc = new XDocument(new XElement("CharacterData")); - foreach (CharacterCampaignData cd in characterData) - { - characterDataDoc.Root.Add(cd.Save()); - } - try - { - characterDataDoc.Save(characterDataPath); - } - catch (Exception e) - { - DebugConsole.ThrowError("Saving multiplayer campaign characters to \"" + characterDataPath + "\" failed!", e); - } - - lastSaveID++; - } } } diff --git a/Barotrauma/BarotraumaShared/Source/GameSettings.cs b/Barotrauma/BarotraumaShared/Source/GameSettings.cs index e8b625621..2fd21c4aa 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSettings.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSettings.cs @@ -36,34 +36,6 @@ namespace Barotrauma public bool VSyncEnabled { get; set; } public bool EnableSplashScreen { get; set; } - - 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; } - - public int ParticleLimit { get; set; } - - public float LightMapScale { get; set; } - public bool SpecularityEnabled { get; set; } - public bool ChromaticAberrationEnabled { get; set; } - - public bool MuteOnFocusLost { get; set; } - - public enum VoiceMode - { - Disabled, - PushToTalk, - Activity - }; - - public VoiceMode VoiceSetting { get; set; } - public string VoiceCaptureDevice { get; set; } - - public int ParticleLimit { get; set; } public int ParticleLimit { get; set; } @@ -196,7 +168,7 @@ namespace Barotrauma } } - private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f; + private float soundVolume = 0.5f, musicVolume = 0.3f, voiceChatVolume = 0.5f, microphoneVolume = 1.0f; public float SoundVolume { @@ -239,6 +211,14 @@ namespace Barotrauma } } + public float MicrophoneVolume + { + get { return microphoneVolume; } + set + { + microphoneVolume = MathHelper.Clamp(value, 0.1f, 5.0f); + } + } public string Language { get { return TextManager.Language; } @@ -397,10 +377,6 @@ 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]; keyMapping[(int)InputType.Up] = new KeyOrMouse(Keys.W); keyMapping[(int)InputType.Down] = new KeyOrMouse(Keys.S); @@ -509,70 +485,6 @@ namespace Barotrauma SelectedContentPackages.Add(matchingContentPackage); } break; - } - } - - TextManager.LoadTextPacks(SelectedContentPackages); - - //display error messages after all content packages have been loaded - //to make sure the package that contains text files has been loaded before we attempt to use TextManager - foreach (string missingPackagePath in missingPackagePaths) - { - DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath)); - } - foreach (ContentPackage incompatiblePackage in incompatiblePackages) - { - DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") - .Replace("[packagename]", incompatiblePackage.Name) - .Replace("[packageversion]", incompatiblePackage.GameVersion.ToString()) - .Replace("[gameversion]", GameMain.Version.ToString())); - } - foreach (ContentPackage contentPackage in SelectedContentPackages) - { - foreach (ContentFile file in contentPackage.Files) - { - if (!System.IO.File.Exists(file.Path)) - { - DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found."); - continue; - } - ToolBox.IsProperFilenameCase(file.Path); - } - } - - TextManager.LoadTextPacks(SelectedContentPackages); - - //display error messages after all content packages have been loaded - //to make sure the package that contains text files has been loaded before we attempt to use TextManager - foreach (string missingPackagePath in missingPackagePaths) - { - DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath)); - } - foreach (ContentPackage incompatiblePackage in incompatiblePackages) - { - DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") - .Replace("[packagename]", incompatiblePackage.Name) - .Replace("[packageversion]", incompatiblePackage.GameVersion.ToString()) - .Replace("[gameversion]", GameMain.Version.ToString())); - } - foreach (ContentPackage contentPackage in SelectedContentPackages) - { - foreach (ContentFile file in contentPackage.Files) - { - if (!System.IO.File.Exists(file.Path)) - { - DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found."); - continue; - } - ToolBox.IsProperFilenameCase(file.Path); - } - } - if (!SelectedContentPackages.Any()) - { - var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage); - if (availablePackage != null) - { - SelectedContentPackages.Add(availablePackage); } } @@ -1021,369 +933,7 @@ namespace Barotrauma gMode = new XElement("graphicsmode"); doc.Root.Add(gMode); } - if (GraphicsWidth == 0 || GraphicsHeight == 0) - { - gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode)); - } - else - { - gMode.ReplaceAttributes( - new XAttribute("width", GraphicsWidth), - new XAttribute("height", GraphicsHeight), - new XAttribute("vsync", VSyncEnabled), - new XAttribute("displaymode", windowMode)); - } - XElement gSettings = doc.Root.Element("graphicssettings"); - if (gSettings == null) - { - gSettings = new XElement("graphicssettings"); - doc.Root.Add(gSettings); - } - - gSettings.ReplaceAttributes( - new XAttribute("particlelimit", ParticleLimit), - new XAttribute("lightmapscale", LightMapScale), - new XAttribute("specularity", SpecularityEnabled), - new XAttribute("chromaticaberration", ChromaticAberrationEnabled), - new XAttribute("losmode", LosMode), - new XAttribute("hudscale", HUDScale), - new XAttribute("inventoryscale", InventoryScale)); - - foreach (ContentPackage contentPackage in SelectedContentPackages) - { - if (contentPackage.Path.Contains(vanillaContentPackagePath)) - { - doc.Root.Add(new XElement("contentpackage", new XAttribute("path", contentPackage.Path))); - break; - } - } - - var keyMappingElement = new XElement("keymapping"); - doc.Root.Add(keyMappingElement); - for (int i = 0; i < keyMapping.Length; i++) - { - if (keyMapping[i].MouseButton == null) - { - keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].Key)); - } - else - { - keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].MouseButton)); - } - } - - var gameplay = new XElement("gameplay"); - var jobPreferences = new XElement("jobpreferences"); - foreach (string jobName in JobPreferences) - { - jobPreferences.Add(new XElement("job", new XAttribute("identifier", jobName))); - } - gameplay.Add(jobPreferences); - doc.Root.Add(gameplay); - - var playerElement = new XElement("player", - new XAttribute("name", defaultPlayerName ?? ""), - new XAttribute("headindex", CharacterHeadIndex), - new XAttribute("gender", CharacterGender), - new XAttribute("race", CharacterRace), - new XAttribute("hairindex", CharacterHairIndex), - new XAttribute("beardindex", CharacterBeardIndex), - new XAttribute("moustacheindex", CharacterMoustacheIndex), - new XAttribute("faceattachmentindex", CharacterFaceAttachmentIndex)); - doc.Root.Add(playerElement); - - XmlWriterSettings settings = new XmlWriterSettings - { - Indent = true, - OmitXmlDeclaration = true, - NewLineOnAttributes = true - }; - - try - { - using (var writer = XmlWriter.Create(savePath, settings)) - { - doc.WriteTo(writer); - writer.Flush(); - } - } - catch (Exception e) - { - 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); - } - } - #endregion - - #region Load PlayerConfig - // TODO: DRY - public void LoadPlayerConfig() - { - XDocument doc = XMLExtensions.LoadXml(playerSavePath); - - if (doc == null || doc.Root == null) - { - ShowUserStatisticsPrompt = true; - SaveNewPlayerConfig(); - return; - } - - Language = doc.Root.GetAttributeString("language", Language); - AutoCheckUpdates = doc.Root.GetAttributeBool("autocheckupdates", AutoCheckUpdates); - sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", true); - - XElement graphicsMode = doc.Root.Element("graphicsmode"); - GraphicsWidth = graphicsMode.GetAttributeInt("width", GraphicsWidth); - GraphicsHeight = graphicsMode.GetAttributeInt("height", GraphicsHeight); - VSyncEnabled = graphicsMode.GetAttributeBool("vsync", VSyncEnabled); - - XElement graphicsSettings = doc.Root.Element("graphicssettings"); - ParticleLimit = graphicsSettings.GetAttributeInt("particlelimit", ParticleLimit); - LightMapScale = MathHelper.Clamp(graphicsSettings.GetAttributeFloat("lightmapscale", LightMapScale), 0.1f, 1.0f); - SpecularityEnabled = graphicsSettings.GetAttributeBool("specularity", SpecularityEnabled); - ChromaticAberrationEnabled = graphicsSettings.GetAttributeBool("chromaticaberration", ChromaticAberrationEnabled); - HUDScale = graphicsSettings.GetAttributeFloat("hudscale", HUDScale); - InventoryScale = graphicsSettings.GetAttributeFloat("inventoryscale", InventoryScale); - var losModeStr = graphicsSettings.GetAttributeString("losmode", "Transparent"); - if (!Enum.TryParse(losModeStr, out losMode)) - { - losMode = LosMode.Transparent; - } - -#if CLIENT - if (GraphicsWidth == 0 || GraphicsHeight == 0) - { - GraphicsWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width; - GraphicsHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height; - } -#endif - - var windowModeStr = graphicsMode.GetAttributeString("displaymode", "Fullscreen"); - if (!Enum.TryParse(windowModeStr, out windowMode)) - { - windowMode = WindowMode.Fullscreen; - } - - XElement audioSettings = doc.Root.Element("audio"); - if (audioSettings != null) - { - SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume); - MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume); - VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume); - string voiceSettingStr = audioSettings.GetAttributeString("voicesetting", "Disabled"); - VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", ""); - NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", -45); - var voiceSetting = VoiceMode.Disabled; - if (Enum.TryParse(voiceSettingStr, out voiceSetting)) - { - VoiceSetting = voiceSetting; - } - } - - useSteamMatchmaking = doc.Root.GetAttributeBool("usesteammatchmaking", useSteamMatchmaking); - requireSteamAuthentication = doc.Root.GetAttributeBool("requiresteamauthentication", requireSteamAuthentication); - - EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen); - - AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount); - - foreach (XElement subElement in doc.Root.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "keymapping": - foreach (XAttribute attribute in subElement.Attributes()) - { - if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType)) - { - if (int.TryParse(attribute.Value.ToString(), out int mouseButton)) - { - keyMapping[(int)inputType] = new KeyOrMouse(mouseButton); - } - else - { - if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key)) - { - keyMapping[(int)inputType] = new KeyOrMouse(key); - } - } - } - } - break; - case "gameplay": - jobPreferences = new List(); - foreach (XElement ele in subElement.Element("jobpreferences").Elements("job")) - { - string jobIdentifier = ele.GetAttributeString("identifier", ""); - if (string.IsNullOrEmpty(jobIdentifier)) continue; - jobPreferences.Add(jobIdentifier); - } - break; - case "player": - defaultPlayerName = subElement.GetAttributeString("name", defaultPlayerName); - CharacterHeadIndex = subElement.GetAttributeInt("headindex", CharacterHeadIndex); - if (Enum.TryParse(subElement.GetAttributeString("gender", "none"), true, out Gender g)) - { - CharacterGender = g; - } - if (Enum.TryParse(subElement.GetAttributeString("race", "white"), true, out Race r)) - { - CharacterRace = r; - } - else - { - CharacterRace = Race.White; - } - CharacterHairIndex = subElement.GetAttributeInt("hairindex", CharacterHairIndex); - CharacterBeardIndex = subElement.GetAttributeInt("beardindex", CharacterBeardIndex); - CharacterMoustacheIndex = subElement.GetAttributeInt("moustacheindex", CharacterMoustacheIndex); - CharacterFaceAttachmentIndex = subElement.GetAttributeInt("faceattachmentindex", CharacterFaceAttachmentIndex); - break; - case "tutorials": - foreach (XElement tutorialElement in subElement.Elements()) - { - CompletedTutorialNames.Add(tutorialElement.GetAttributeString("name", "")); - } - break; - } - } - - foreach (InputType inputType in Enum.GetValues(typeof(InputType))) - { - if (keyMapping[(int)inputType] == null) - { - DebugConsole.ThrowError("Key binding for the input type \"" + inputType + " not set!"); - keyMapping[(int)inputType] = new KeyOrMouse(Keys.D1); - } - } - - UnsavedSettings = false; - - selectedContentPackagePaths = new HashSet(); - - foreach (XElement subElement in doc.Root.Elements()) - { - switch (subElement.Name.ToString().ToLowerInvariant()) - { - case "contentpackage": - string path = System.IO.Path.GetFullPath(subElement.GetAttributeString("path", "")); - selectedContentPackagePaths.Add(path); - break; - } - } - - LoadContentPackages(selectedContentPackagePaths); - } - - public void ReloadContentPackages() - { - LoadContentPackages(selectedContentPackagePaths); - } - - private void LoadContentPackages(IEnumerable contentPackagePaths) - { - var missingPackagePaths = new List(); - var incompatiblePackages = new List(); - SelectedContentPackages.Clear(); - foreach (string path in contentPackagePaths) - { - var matchingContentPackage = ContentPackage.List.Find(cp => System.IO.Path.GetFullPath(cp.Path) == path); - - if (matchingContentPackage == null) - { - missingPackagePaths.Add(path); - } - else if (!matchingContentPackage.IsCompatible()) - { - incompatiblePackages.Add(matchingContentPackage); - } - else - { - SelectedContentPackages.Add(matchingContentPackage); - } - } - - TextManager.LoadTextPacks(SelectedContentPackages); - - foreach (ContentPackage contentPackage in SelectedContentPackages) - { - foreach (ContentFile file in contentPackage.Files) - { - if (!System.IO.File.Exists(file.Path)) - { - DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found."); - continue; - } - ToolBox.IsProperFilenameCase(file.Path); - } - } - if (!SelectedContentPackages.Any()) - { - var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage); - if (availablePackage != null) - { - SelectedContentPackages.Add(availablePackage); - } - } - - //save to get rid of the invalid selected packages in the config file - if (missingPackagePaths.Count > 0 || incompatiblePackages.Count > 0) { SaveNewPlayerConfig(); } - - //display error messages after all content packages have been loaded - //to make sure the package that contains text files has been loaded before we attempt to use TextManager - foreach (string missingPackagePath in missingPackagePaths) - { - DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath)); - } - foreach (ContentPackage incompatiblePackage in incompatiblePackages) - { - DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage") - .Replace("[packagename]", incompatiblePackage.Name) - .Replace("[packageversion]", incompatiblePackage.GameVersion.ToString()) - .Replace("[gameversion]", GameMain.Version.ToString())); - } - } - #endregion - - #region Save PlayerConfig - public void SaveNewPlayerConfig() - { - XDocument doc = new XDocument(); - UnsavedSettings = false; - - if (doc.Root == null) - { - doc.Add(new XElement("config")); - } - - doc.Root.Add( - new XAttribute("language", TextManager.Language), - new XAttribute("masterserverurl", MasterServerUrl), - new XAttribute("autocheckupdates", AutoCheckUpdates), - new XAttribute("musicvolume", musicVolume), - new XAttribute("soundvolume", soundVolume), - new XAttribute("verboselogging", VerboseLogging), - new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs), - new XAttribute("enablesplashscreen", EnableSplashScreen), - new XAttribute("usesteammatchmaking", useSteamMatchmaking), - new XAttribute("quickstartsub", QuickStartSubmarineName), - new XAttribute("requiresteamauthentication", requireSteamAuthentication), - new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems), - new XAttribute("aimassistamount", aimAssistAmount)); - - if (!ShowUserStatisticsPrompt) - { - doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics)); - } - - XElement gMode = doc.Root.Element("graphicsmode"); - if (gMode == null) - { - gMode = new XElement("graphicsmode"); - doc.Root.Add(gMode); - } if (GraphicsWidth == 0 || GraphicsHeight == 0) { gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode)); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs index df799439a..89ee1b073 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/DockingPort.cs @@ -641,7 +641,7 @@ namespace Barotrauma.Items.Components if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]); } //make sure the left hull is linked to the gap first (gap logic assumes that the first hull is the one to the left) - if (doorGap.linkedTo[0].Rect.X > doorGap.linkedTo[1].Rect.X) + if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].Rect.X > doorGap.linkedTo[1].Rect.X) { var temp = doorGap.linkedTo[0]; doorGap.linkedTo[0] = doorGap.linkedTo[1]; @@ -659,7 +659,7 @@ namespace Barotrauma.Items.Components if (!doorGap.linkedTo.Contains(hulls[1])) doorGap.linkedTo.Add(hulls[1]); } //make sure the upper hull is linked to the gap first (gap logic assumes that the first hull is above the second one) - if (doorGap.linkedTo[0].Rect.Y < doorGap.linkedTo[1].Rect.Y) + if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].Rect.Y < doorGap.linkedTo[1].Rect.Y) { var temp = doorGap.linkedTo[0]; doorGap.linkedTo[0] = doorGap.linkedTo[1]; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs index 1fe61d323..91d669c94 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Door.cs @@ -51,6 +51,11 @@ namespace Barotrauma.Items.Components public PhysicsBody Body { get; private set; } + private float RepairThreshold + { + get { return item.GetComponent()?.ShowRepairUIThreshold ?? 0.0f; } + } + private float stuck; [Serialize(0.0f, false)] public float Stuck @@ -207,7 +212,7 @@ namespace Barotrauma.Items.Components public override bool HasRequiredItems(Character character, bool addMessage) { - if (item.Condition <= 0.0f) return true; //For repairing + if (item.Condition <= RepairThreshold) return true; //For repairing //this is a bit pointless atm because if canBePicked is false it won't allow you to do Pick() anyway, however it's still good for future-proofing. return requiredItems.Any() ? base.HasRequiredItems(character, addMessage) : canBePicked; @@ -215,12 +220,12 @@ namespace Barotrauma.Items.Components public override bool Pick(Character picker) { - return item.Condition <= 0.0f ? true : base.Pick(picker); + return item.Condition <= RepairThreshold ? true : base.Pick(picker); } public override bool OnPicked(Character picker) { - if (item.Condition <= 0.0f) return true; //repairs + if (item.Condition <= RepairThreshold) return true; //repairs SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function #if CLIENT @@ -232,7 +237,7 @@ namespace Barotrauma.Items.Components public override bool Select(Character character) { //can only be selected if the item is broken - return item.Condition <= 0.0f; + return item.Condition <= RepairThreshold; } public override void Update(float deltaTime, Camera cam) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs index 2885ca95f..37c046772 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RepairTool.cs @@ -141,6 +141,7 @@ namespace Barotrauma.Items.Components partial void UseProjSpecific(float deltaTime); + private List fireSourcesInRange = new List(); private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List ignoredBodies) { var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; @@ -159,7 +160,7 @@ namespace Barotrauma.Items.Components if (ExtinguishAmount > 0.0f && item.CurrentHull != null) { - List fireSourcesInRange = new List(); + fireSourcesInRange.Clear(); //step along the ray in 10% intervals, collecting all fire sources in the range for (float x = 0.0f; x <= Submarine.LastPickedFraction; x += 0.1f) { @@ -200,7 +201,7 @@ namespace Barotrauma.Items.Components FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex); targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess, user); - + //if the next section is small enough, apply the effect to it as well //(to make it easier to fix a small "left-over" section) for (int i = -1; i < 2; i += 2) @@ -252,11 +253,11 @@ namespace Barotrauma.Items.Components } } - partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex); partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter); partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition); + private float sinTime; public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { Gap leak = objective.OperateTarget as Gap; @@ -280,18 +281,26 @@ namespace Barotrauma.Items.Components { Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f); standPos = leak.WorldPosition + standPos * Range; - // TODO: check if too close to the stand pos -> move away so that the tool can hit the target and not through it? Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition); character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2); } else { - // TODO: sometimes stuck here, if too close to the target - //close enough -> stop moving - character.AIController.SteeringManager.Reset(); + if (dist < Range / 2) + { + // Too close -> steer away + character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2); + } + else if (!character.IsClimbing) + { + // Close enough -> stop if not in ladders. + // In ladders, we most likely want to move back and forth, because we cannot aim -> if the leak is on the side, it should get fixed. + character.AIController.SteeringManager.Reset(); + } } - character.CursorPosition = leak.Position; + sinTime += deltaTime; + character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist); character.SetInput(InputType.Aim, false, true); // Press the trigger only when the tool is approximately facing the target. @@ -300,12 +309,17 @@ namespace Barotrauma.Items.Components { Use(deltaTime, character); } + else + { + sinTime -= deltaTime * 2; + } - // TODO: fix until the wall is fixed? - bool leakFixed = leak.Open <= 0.0f || leak.Removed; + bool leakFixed = (leak.Open <= 0.0f || leak.Removed) && + (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1); if (leakFixed && leak.FlowTargetHull != null) { + sinTime = 0; if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f)) { character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs index 24018f2f9..cf13d1288 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/ItemComponent.cs @@ -535,7 +535,6 @@ namespace Barotrauma.Items.Components GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return 0.0f; } - float average = skillSuccessSum / requiredSkills.Count; float skillSuccessSum = 0.0f; for (int i = 0; i < requiredSkills.Count; i++) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs index ae727e482..010f24173 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Engine.cs @@ -155,29 +155,5 @@ namespace Barotrauma.Items.Components } } } - - public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) - { - //force can only be adjusted at 10% intervals -> no need for more accuracy than this - msg.WriteRangedInteger(-10, 10, (int)(targetForce / 10.0f)); - } - - public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) - { - float newTargetForce = msg.ReadRangedInteger(-10, 10) * 10.0f; - - if (item.CanClientAccess(c)) - { - if (Math.Abs(newTargetForce - targetForce) > 0.01f) - { - GameServer.Log(c.Character.LogName + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction); - } - - targetForce = newTargetForce; - } - - //notify all clients of the changed state - item.CreateServerEvent(this); - } } } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs index 0155f497f..021ad6197 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs @@ -160,7 +160,6 @@ namespace Barotrauma.Items.Components public override void Update(float deltaTime, Camera cam) { UpdateOnActiveEffects(deltaTime); - if (AITarget != null) AITarget.Enabled = voltage > minVoltage || powerConsumption <= 0.0f; #if CLIENT light.ParentSub = item.Submarine; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs index 29fda4341..8b89d237a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/Wire.cs @@ -177,12 +177,21 @@ namespace Barotrauma.Items.Components Vector2 nodePos = refSub == null ? newConnection.Item.Position : newConnection.Item.Position - refSub.HiddenSubPosition; - - + if (nodes.Count > 0 && nodes[0] == nodePos) break; if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) break; - if (i == 0) + //make sure we place the node at the correct end of the wire (the end that's closest to the new node pos) + int newNodeIndex = 0; + if (nodes.Count > 1) + { + if (Vector2.DistanceSquared(nodes[nodes.Count-1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos)) + { + newNodeIndex = nodes.Count; + } + } + + if (newNodeIndex == 0) { nodes.Insert(0, nodePos); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs index a74c4f4e2..3dfbf16d9 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs @@ -433,9 +433,10 @@ namespace Barotrauma.Items.Components if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.ToLowerInvariant() != "fireatwill")) { ItemContainer container = null; + Item containerItem = null; foreach (MapEntity e in item.linkedTo) { - var containerItem = e as Item; + containerItem = e as Item; if (containerItem == null) continue; container = containerItem.GetComponent(); @@ -453,7 +454,7 @@ namespace Barotrauma.Items.Components var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container); character?.Speak(TextManager.Get("DialogLoadTurret").Replace("[itemname]", item.Name), null, 0.0f, "loadturret", 30.0f); containShellObjective.MinContainedAmount = usableProjectileCount + 1; - containShellObjective.IgnoreAlreadyContainedItems = true; + containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; objective.AddSubObjective(containShellObjective); return false; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 29915b1d1..beb21b83d 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -1631,12 +1631,13 @@ namespace Barotrauma SerializableProperty property = extraData[1] as SerializableProperty; if (property != null) { + var propertyOwner = allProperties.Find(p => p.Second == property); if (allProperties.Count > 1) { msg.WriteRangedInteger(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property)); } - object value = property.GetValue(this); + object value = property.GetValue(propertyOwner.First); if (value is string) { msg.Write((string)value); diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs index 9b11b8c59..42039e967 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/LocationType.cs @@ -89,7 +89,9 @@ namespace Barotrauma } CommonnessPerZone[zoneIndex] = zoneCommonness; } - catch (Exception e) + + hireableJobs = new List>(); + foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { diff --git a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs index c3d5d2926..ba0ba04d5 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Map/Map.cs @@ -269,9 +269,6 @@ namespace Barotrauma } } - //remove orphans - Locations.RemoveAll(c => !connectedLocations.Contains(c)); - for (int i = connections.Count - 1; i >= 0; i--) { i = Math.Min(i, connections.Count - 1); @@ -428,7 +425,6 @@ namespace Barotrauma GameAnalyticsManager.AddErrorEventOnce("Map.SelectLocation:LocationNotFound", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } - CurrentLocation.SelectedMissionIndex = missionIndex; SelectedLocation = location; SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation)); diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index 763b0b250..53466bc5a 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -134,7 +134,7 @@ namespace Barotrauma public HashSet Tags { - get { return prefab; } + get { return prefab.Tags; } } protected Color spriteColor; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 44fbb7299..1e7294d22 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -523,30 +523,6 @@ namespace Barotrauma { maxX = Math.Min(maxX, ruin.Area.X - 100.0f); } - else - { - maxX = Math.Min(maxX, ruin.Area.X - 100.0f); - } - } - - if (minX < 0.0f && maxX > Level.Loaded.Size.X) - { - //no walls found at either side, just use the initial spawnpos and hope for the best - } - else if (minX < 0) - { - //no wall found at the left side, spawn to the left from the right-side wall - spawnPos.X = maxX - minWidth - 100.0f; - } - else if (maxX > Level.Loaded.Size.X) - { - //no wall found at right side, spawn to the right from the left-side wall - spawnPos.X = minX + minWidth + 100.0f; - } - else - { - //walls found at both sides, use their midpoint - spawnPos.X = (minX + maxX) / 2; } if (minX < 0.0f && maxX > Level.Loaded.Size.X) @@ -1016,26 +992,6 @@ namespace Barotrauma return closest; } - public List GetHulls(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Hull.hullList); - public List GetGaps(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Gap.GapList); - public List GetItems(bool alsoFromConnectedSubs) => GetEntities(alsoFromConnectedSubs, Item.ItemList); - - public List GetEntities(bool includingConnectedSubs, List list) where T : MapEntity - { - return list.FindAll(e => IsEntityFoundOnThisSub(e, includingConnectedSubs)); - } - - public bool IsEntityFoundOnThisSub(MapEntity entity, bool includingConnectedSubs) - { - if (entity.Submarine == this) { return true; } - if (entity.Submarine == null) { return false; } - if (includingConnectedSubs) - { - return GetConnectedSubs().Any(s => s == entity.Submarine && entity.Submarine.TeamID == TeamID); - } - return false; - } - /// /// Returns true if the sub is same as the other. /// diff --git a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs index 0a1b5fa21..a0d500ade 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs @@ -700,6 +700,23 @@ namespace Barotrauma Vector2 impulse = direction * impact * 0.5f; impulse = impulse.ClampLength(5.0f); + if (!MathUtils.IsValid(impulse)) + { + string errorMsg = + "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse + + ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + "."; + if (GameMain.NetworkMember != null) + { + errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; + } + if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg); + GameAnalyticsManager.AddErrorEventOnce( + "SubmarineBody.ApplyImpact:InvalidImpulse", + GameAnalyticsSDK.Net.EGAErrorSeverity.Error, + errorMsg); + return; + } + #if CLIENT if (Character.Controlled != null && Character.Controlled.Submarine == submarine) { diff --git a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs index 2c9f0ed38..9e7228401 100644 --- a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs @@ -522,7 +522,12 @@ namespace Barotrauma string errorMsg = "Attempted to apply invalid " + valueName + " to a physics body (userdata: " + userData + - "), value: " + value + "\n" + Environment.StackTrace; + "), value: " + value; + if (GameMain.NetworkMember != null) + { + errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; + } + errorMsg += "\n" + Environment.StackTrace; if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce( @@ -544,7 +549,12 @@ namespace Barotrauma string errorMsg = "Attempted to apply invalid " + valueName + " to a physics body (userdata: " + userData + - "), value: " + value + "\n" + Environment.StackTrace; + "), value: " + value; + if (GameMain.NetworkMember != null) + { + errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; + } + errorMsg += "\n" + Environment.StackTrace; if (GameSettings.VerboseLogging) DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce( diff --git a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index dac0e7d44..6de3c7927 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -54,7 +54,7 @@ namespace Barotrauma case 6: return PlayerInput.MouseWheelDownClicked(); } - + return false; } @@ -113,14 +113,7 @@ namespace Barotrauma { private bool hit, hitQueue; private bool held, heldQueue; - -#if CLIENT - private InputType inputType; - public Key(InputType inputType) - { - this.inputType = inputType; - } private InputType inputType; @@ -138,17 +131,6 @@ namespace Barotrauma { get { return binding; } } -#endif - - public void SetState() - { - hit = binding.IsHit(); - if (hit) hitQueue = true; - - held = binding.IsDown(); - if (held) heldQueue = true; - } -#endif public void SetState() { diff --git a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs index dd1fbb2e8..55d8c62fe 100644 --- a/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs +++ b/Barotrauma/BarotraumaShared/Source/StatusEffects/StatusEffect.cs @@ -717,11 +717,6 @@ namespace Barotrauma } } } - - bool isNotClient = true; -#if CLIENT - isNotClient = GameMain.Client == null; -#endif if (FireSize > 0.0f && entity != null) { diff --git a/Barotrauma/BarotraumaShared/Source/TextManager.cs b/Barotrauma/BarotraumaShared/Source/TextManager.cs index a4b006c84..1d5d074c8 100644 --- a/Barotrauma/BarotraumaShared/Source/TextManager.cs +++ b/Barotrauma/BarotraumaShared/Source/TextManager.cs @@ -153,49 +153,59 @@ namespace Barotrauma string[] messages = serverMessage.Split('/'); - for (int i = 0; i < messages.Length; i++) + try { - if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate + for (int i = 0; i < messages.Length; i++) { - if (messages[i].Contains(" ")) continue; // Spaces found, do not translate - - string msg = Get(messages[i], true); - - if (msg != null) // If a translation was found, otherwise use the original + if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate { - messages[i] = msg; - } - } - else - { - string[] messageWithVariables = messages[i].Split('~'); - string msg = Get(messageWithVariables[0], true); - - if (msg != null) // If a translation was found, otherwise use the original - { - messages[i] = msg; + if (messages[i].Contains(" ")) continue; // Spaces found, do not translate + string msg = Get(messages[i], true); + if (msg != null) // If a translation was found, otherwise use the original + { + messages[i] = msg; + } } else { - continue; // No translation found, probably caused by player input -> skip variable handling - } + string[] messageWithVariables = messages[i].Split('~'); + string msg = Get(messageWithVariables[0], true); - // First index is always the message identifier -> start at 1 - for (int j = 1; j < messageWithVariables.Length; j++) - { - string[] variableAndValue = messageWithVariables[j].Split('='); - messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]); + if (msg != null) // If a translation was found, otherwise use the original + { + messages[i] = msg; + } + else + { + continue; // No translation found, probably caused by player input -> skip variable handling + } + + // First index is always the message identifier -> start at 1 + for (int j = 1; j < messageWithVariables.Length; j++) + { + string[] variableAndValue = messageWithVariables[j].Split('='); + messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]); + } } } + + string translatedServerMessage = string.Empty; + for (int i = 0; i < messages.Length; i++) + { + translatedServerMessage += messages[i]; + } + return translatedServerMessage; } - string translatedServerMessage = string.Empty; - for (int i = 0; i < messages.Length; i++) + catch (IndexOutOfRangeException exception) { - translatedServerMessage += messages[i]; + string errorMsg = "Failed to translate server message \"" + serverMessage + "\"."; +#if DEBUG + DebugConsole.ThrowError(errorMsg, exception); +#endif + GameAnalyticsManager.AddErrorEventOnce("TextManager.GetServerMessage:" + serverMessage, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + return errorMsg; } - - return translatedServerMessage; } public static bool IsServerMessageWithVariables(string message) diff --git a/Barotrauma/BarotraumaShared/Source/TextPack.cs b/Barotrauma/BarotraumaShared/Source/TextPack.cs index a9cf50356..91366d043 100644 --- a/Barotrauma/BarotraumaShared/Source/TextPack.cs +++ b/Barotrauma/BarotraumaShared/Source/TextPack.cs @@ -33,7 +33,11 @@ namespace Barotrauma texts.Add(infoName, infoList); } - infoList.Add(subElement.ElementInnerText()); + string text = subElement.ElementInnerText(); + text = text.Replace("&", "&"); + text = text.Replace("<", "<"); + text = text.Replace(">", ">"); + infoList.Add(text); } } diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 97196e8ec..d2cd1ff19 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Muikku.sub b/Barotrauma/BarotraumaShared/Submarines/Muikku.sub index 759d33ff4..386de54c9 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Muikku.sub and b/Barotrauma/BarotraumaShared/Submarines/Muikku.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index 6f8e91fd6..3d9685698 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/PAX.sub b/Barotrauma/BarotraumaShared/Submarines/PAX.sub new file mode 100644 index 000000000..58e7cc9a0 Binary files /dev/null and b/Barotrauma/BarotraumaShared/Submarines/PAX.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 72b0194da..a1dfafeea 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,83 @@ +--------------------------------------------------------------------------------------------------------- +v0.8.9.6 +--------------------------------------------------------------------------------------------------------- + +Additions: +- A major overhaul on the crew AI. They should now be much better at executing their tasks, keeping +themselves alive and less likely to get stuck. +- Re-enabled multiplayer campaign. +- New alien ruin art. +- New main menu (still a work in progress). +- New fonts. +- New subs and a new shuttle. +- New husk sprites. +- Improved item/character highlight effect. +- New signal items (divide, multiply, subtract, memory, equals, greater than, color, xor). +- Option to adjust microphone volume in multiplayer. +- Added a console commands for changing the gender and race of the character. +- More intuitive BrokenSprite condition logic: a BrokenSprite with a MaxCondition of 50 will start +fading in at 50 (and be fully visible when the condition drops to 0 or down to the MaxCondition of +the next BrokenSprite). +- Added Mirror X/Y buttons to editing HUDs and tooltips that tell about the keyboard shortcuts. + +Multiplayer fixes: +- Improved item position syncing logic. Less warping or items being impossible to pick up due to +being in a different position server-side. +- Fixed clients not sending a network update to the server when dragging an item out of an inventory +slot, causing situations such as suffocation after dropping a diving suit due to the server not +knowing that you'd dropped it. +- The client hosting a server and spectators don't trigger autorestart. +- The owner of a server is allowed to spectate even if spectating is disallowed in server settings. +- Fixed "play yourself" always toggling to true when a round ends. +- Fixed missing item names in the extra cargo menu. +- Fixed traitor rounds failing to start if the server is not hosted by a client. +- Fixed console command aliases not being taken into account in GameClient.HasConsoleCommandPermission +(meaning that the client needed a permission for each name variant of a command, making it impossible +to for example use "fixwalls" instead of "fixhulls"). +- Made the "control" console command usable to clients. +- Show the "ready to start" tickbox in the server lobby even if the client has the permission to start +the round. +- Fixed server lobby screen not showing the names of the submarines the client doesn't have. +- Fixed inability to select the respawn shuttle as a client host. +- Fixed VoipCapture creating new "could not start voice capture" popups constantly if there's no +suitable capture device. +- Fixed crashing when starting a round if a submarine name contains underscores. +- Fixed clients console errors when attempting to modify the properties of an ItemComponent in-game +(for example, the color of a light component). +- Added some more information to the console messages and error popups when a client gets disconnected. +- Fixed enablecheats command crashing the server if it's used when there are no clients present. + +Misc bugfixes: +- Audio fixes (less snap, crackle and pop). +- Fixed particle "jitter" when the submarine was moving fast. +- Fixed damage modifiers affecting all afflictions if they use affliction types instead of affliction +identifiers. +- Fixed end round vote text going outside the screen if there's a 2-digit amount of votes. +- Fixed StatusEffects only applying afflictions to one limb even if the target is "Character" instead of "Limb". +- Disable audio instead of crashing if no audio device is found. +- Fixed item interfaces getting repositioned every frame when the editing HUD is open. +- Fixed held items clipping with the sleeves of the character (e.g. when holding a revolver while an +uniform is equipped). +- Fixed being able to levitate by spamming the ragdoll button. +- Fixed dead characters draining oxygen tanks inside diving suits/masks. +- Fixed reactor gauges getting messed up if the optimal fission rate is more than 100% (which may happen +if the power consumption is larger than what the reactor can generate). +- Fixed mud raptors not having an inventory (nor lootable items). +- Fixed inability to interact with any items when aim assist is set to 0%. +- Fixed info panel flickering out and Tab getting "inverted" (= info panel shown when tab is not being held) +when selecting crew members in the panel. +- Fixed characters arms occasionally getting stuck above their shoulders. +- Fixed wire nodes occasionally being created at the wrong end of a wire (e.g. when moving a wire between +connections in a connection panel, the wire stretched from the device at the other end of the wire to +the device that's being rewired). + +Misc: +- Changed the way arguments are given to the "setclientcharacter" command (no semicolon to separate the +names, quotation marks have to be used for multi-word names just like with any other command). +- Show the amount of credits in the crew tab of the campaign menu. +- Don't spawn new monsters if docked to the start outpost or within 50 meters of the start/end of the level. +- The diving knife now causes also internal damage besides bleeding. + --------------------------------------------------------------------------------------------------------- v0.8.9.5 --------------------------------------------------------------------------------------------------------- diff --git a/git-strip-merge b/git-strip-merge new file mode 100644 index 000000000..318a93d46 --- /dev/null +++ b/git-strip-merge @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# +# git-strip-merge - a git-merge that delete files on branch before merging +# +# Copyright (C) 2012 Rodrigo Silva (MestreLion) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not see +# +# Answer for "How to setup a git driver to ignore a folder on merge?" +# See http://stackoverflow.com/questions/3111515 + +#Defaults: +msgcommit="remove files from '' before merge" +msgmerge="Merge stripped branch ''" +verbose=0 +quiet=(--quiet) + +usage() { + cat <<- USAGE + Usage: $myname [git-merge options] [-M ] FILE... + USAGE + if [[ "$1" ]] ; then + cat >&2 <<- USAGE + Try '$myname --help' for more information. + USAGE + exit 1 + fi + cat <<-USAGE + git-merge that delete files on "foreign" before merging + Useful for ignoring a folder in before merging it with + current branch. Works by deleting FILE(S) in a detached commit based + on , and then performing the merge of this new commit in the + current branch. Note that is not changed by this procedure. + Also note that may actually be any reference, like a tag, + or a remote branch, or even a commit SHA. + For more information, see + Options: + -h, --help + show this page. + -v, --verbose + do not use -q to suppress normal output of internal steps from git + checkout, rm, commit. By default, only git merge output is shown. + Errors, however, are never suppressed + -M , --msgcommit= + message for the removal commit in . Not to be confused + with the message of the merge commit, which is set by -m. Default + message is: "$msgcommit" + -m , --message= + message for the merge commit. Since we are not merging + directly, but rather a detached commit based on it, we forge a + message similar to git's default for a branch merge. Otherwise + git would use in message the full and ugly SHA1 of our commit. + Default message is: "$msgmerge" + For both commit messages, the token "" is replaced for the + actual name. + Additional options are passed unchecked to git merge. + All options must precede and FILE(s), except -h and --help + that may appear anywhere on the command line. + Example: + $myname design "photoshop/*" + Copyright (C) 2012 Rodrigo Silva (MestreLion) + License: GPLv3 or later. See + USAGE + exit 0 +} + +# Helper functions +myname="${0##*/}" +argerr() { printf "%s: %s\n" "${0##*/}" "${1:-error}" >&2 ; usage 1 ; } +invalid() { argerr "invalid option: $1" ; } +missing() { argerr "missing ${2:+$2 }operand${1:+ from $1}." ; } + +# Option handling +files=() +mergeopts=() +for arg in "$@"; do case "$arg" in -h|--help) usage ;; esac; done +while (( $# )); do + case "$1" in + -v|--verbose ) verbose=1 ;; + -M ) shift ; msgcommit=$1 ;; + -m ) shift ; msgmerge=$1 ;; + --msgcommit=* ) msgcommit=${1#*=} ;; + --message=* ) msgmerge=${1#*=} ;; + -* ) mergeopts+=( "$1" ) ;; + * ) branch="$1" + shift ; break ;; + esac + shift +done +files+=( "$@" ) + +# Argument handling + +msgcommit=${msgcommit///$branch} +msgmerge=${msgmerge///$branch} + +[[ "$msgcommit" ]] || missing "msgcommit" "MSG" +[[ "$branch" ]] || missing "" "" +(( ${#files[@]} )) || missing "" "FILE" + +((verbose)) && quiet=() + +# Here the fun begins... +gitsha() { git rev-parse "$1" ; } +gitbranch() { + git symbolic-ref "$1" 2> /dev/null | sed 's/refs\/heads\///' || + gitsha "$1" +} + +original=$(gitbranch HEAD) +branchsha=$(gitsha "$branch") + +logmsg="$(git log .."$branchsha")" + +trap 'git checkout --quiet "$original"' EXIT + +git checkout "$branchsha" "${quiet[@]}" && +git rm -rf -r "${files[@]}" "${quiet[@]}" && +git commit -m "$msgcommit" "${quiet[@]}" && +newsha=$(gitsha HEAD) && +git checkout "$original" "${quiet[@]}" && +#original +#git merge -m "$msgmerge" "${mergeopts[@]}" "$newsha" + +#baro additions +git merge -m "$msgmerge" "--squash" "$newsha" +git commit -m "$logmsg" + +#./git-strip-merge --no-commit --no-ff barotrauma-development/dev -f Barotrauma/BarotraumaShared/Content/*