From f3bea2702b2ffd0e290c9ba032664a9fd1f3a13e Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 9 Nov 2016 20:00:31 +0200 Subject: [PATCH 01/57] TigerThresher -> Tigerthresher --- .../damagedtigerthresher.png | Bin .../tigerthresher.png | Bin .../tigerthresher.xml | 28 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) rename Subsurface/Content/Characters/{TigerThresher => Tigerthresher}/damagedtigerthresher.png (100%) rename Subsurface/Content/Characters/{TigerThresher => Tigerthresher}/tigerthresher.png (100%) rename Subsurface/Content/Characters/{TigerThresher => Tigerthresher}/tigerthresher.xml (78%) diff --git a/Subsurface/Content/Characters/TigerThresher/damagedtigerthresher.png b/Subsurface/Content/Characters/Tigerthresher/damagedtigerthresher.png similarity index 100% rename from Subsurface/Content/Characters/TigerThresher/damagedtigerthresher.png rename to Subsurface/Content/Characters/Tigerthresher/damagedtigerthresher.png diff --git a/Subsurface/Content/Characters/TigerThresher/tigerthresher.png b/Subsurface/Content/Characters/Tigerthresher/tigerthresher.png similarity index 100% rename from Subsurface/Content/Characters/TigerThresher/tigerthresher.png rename to Subsurface/Content/Characters/Tigerthresher/tigerthresher.png diff --git a/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml b/Subsurface/Content/Characters/Tigerthresher/tigerthresher.xml similarity index 78% rename from Subsurface/Content/Characters/TigerThresher/tigerthresher.xml rename to Subsurface/Content/Characters/Tigerthresher/tigerthresher.xml index 30083658c..1c9f3935c 100644 --- a/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml +++ b/Subsurface/Content/Characters/Tigerthresher/tigerthresher.xml @@ -11,45 +11,45 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + From 50a770a2a6f0e48c67e36865ab7ab511f3c73594 Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 9 Nov 2016 21:01:38 +0200 Subject: [PATCH 02/57] v0.5.3.3 + removed unnecessary EventInput code from the linux build, null check before disposing sprite texture --- Subsurface/Properties/AssemblyInfo.cs | 4 +- Subsurface/Source/EventInput/EventInput.cs | 98 +++++++++++----------- Subsurface/Source/Sprite.cs | 6 +- Subsurface/changelog.txt | 11 +++ 4 files changed, 68 insertions(+), 51 deletions(-) diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs index 057d6e239..c7e4b6961 100644 --- a/Subsurface/Properties/AssemblyInfo.cs +++ b/Subsurface/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.5.3.2")] -[assembly: AssemblyFileVersion("0.5.3.2")] +[assembly: AssemblyVersion("0.5.3.3")] +[assembly: AssemblyFileVersion("0.5.3.3")] diff --git a/Subsurface/Source/EventInput/EventInput.cs b/Subsurface/Source/EventInput/EventInput.cs index 1e51273fa..7965109ad 100644 --- a/Subsurface/Source/EventInput/EventInput.cs +++ b/Subsurface/Source/EventInput/EventInput.cs @@ -6,7 +6,7 @@ using Microsoft.Xna.Framework.Input; namespace EventInput { - +#if WINDOWS public class KeyboardLayout { const uint KLF_ACTIVATE = 1; //activate the layout @@ -16,14 +16,14 @@ namespace EventInput [DllImport("user32.dll")] private static extern long LoadKeyboardLayout( - string pwszKLID, // input locale identifier - uint Flags // input locale identifier options - ); + string pwszKLID, // input locale identifier + uint Flags // input locale identifier options + ); [DllImport("user32.dll")] private static extern long GetKeyboardLayoutName( - StringBuilder pwszKLID //[out] string that receives the name of the locale identifier - ); + StringBuilder pwszKLID //[out] string that receives the name of the locale identifier + ); public static string getName() { @@ -32,6 +32,7 @@ namespace EventInput return name.ToString(); } } +#endif public class CharacterEventArgs : EventArgs { @@ -115,9 +116,12 @@ namespace EventInput /// public static event KeyEventHandler KeyUp; + static bool initialized; + +#if WINDOWS delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); - static bool initialized; + static IntPtr prevWndProc; static WndProc hookProcDelegate; static IntPtr hIMC; @@ -145,7 +149,7 @@ namespace EventInput [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - +#endif /// /// Initialize the TextInput with the given GameWindow. @@ -157,15 +161,15 @@ namespace EventInput { return; } - //throw new InvalidOperationException("TextInput.Initialize can only be called once!"); - hookProcDelegate = HookProc; #if WINDOWS - prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC, - (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate)); + hookProcDelegate = HookProc; - hIMC = ImmGetContext(window.Handle); -#elif LINUX + prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC, + (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate)); + + hIMC = ImmGetContext(window.Handle); +#else window.TextInput += ReceiveInput; #endif @@ -181,50 +185,48 @@ namespace EventInput { if (CharEntered != null) CharEntered(null, new CharacterEventArgs(character, 0)); } - +#if WINDOWS static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { - IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam); + IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam); - switch (msg) - { - case WM_GETDLGCODE: - returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS); - break; + switch (msg) + { + case WM_GETDLGCODE: + returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS); + break; - case WM_KEYDOWN: - if (KeyDown != null) - KeyDown(null, new KeyEventArgs((Keys)wParam)); + case WM_KEYDOWN: + if (KeyDown != null) + KeyDown(null, new KeyEventArgs((Keys)wParam)); - break; + break; - case WM_KEYUP: - if (KeyUp != null) - KeyUp(null, new KeyEventArgs((Keys)wParam)); - break; + case WM_KEYUP: + if (KeyUp != null) + KeyUp(null, new KeyEventArgs((Keys)wParam)); + break; - case WM_CHAR: - if (CharEntered != null) - CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32())); - break; + case WM_CHAR: + if (CharEntered != null) + CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32())); + break; - case WM_IME_SETCONTEXT: - if (wParam.ToInt32() == 1) - ImmAssociateContext(hWnd, hIMC); - break; - - case WM_INPUTLANGCHANGE: - ImmAssociateContext(hWnd, hIMC); - returnCode = (IntPtr)1; - break; - } - - - - return returnCode; + case WM_IME_SETCONTEXT: + if (wParam.ToInt32() == 1) + ImmAssociateContext(hWnd, hIMC); + break; + case WM_INPUTLANGCHANGE: + ImmAssociateContext(hWnd, hIMC); + returnCode = (IntPtr)1; + break; } + + return returnCode; + } + +#endif } - } \ No newline at end of file diff --git a/Subsurface/Source/Sprite.cs b/Subsurface/Source/Sprite.cs index b0b41696f..2f377e951 100644 --- a/Subsurface/Source/Sprite.cs +++ b/Subsurface/Source/Sprite.cs @@ -379,7 +379,11 @@ namespace Barotrauma } //if not, free the texture - texture.Dispose(); + if (texture != null) + { + texture.Dispose(); + texture = null; + } } } diff --git a/Subsurface/changelog.txt b/Subsurface/changelog.txt index 2493dfda0..c010737b6 100644 --- a/Subsurface/changelog.txt +++ b/Subsurface/changelog.txt @@ -1,3 +1,14 @@ +--------------------------------------------------------------------------------------------------------- +v0.5.3.3 +--------------------------------------------------------------------------------------------------------- + +- fixed a bug that caused crashes after a husk-infected player died +- disabled the "zoom effect" when under pressure as a huskified human +- only a limited number of messages are kept in the debug console (prevents performance issues if large +amounts of messages are added) +- some item and electricity logic optimization +- fixed "sprite tigerthresher not found" errors in the Linux version + --------------------------------------------------------------------------------------------------------- v0.5.3.2 --------------------------------------------------------------------------------------------------------- From 4bb8531775ece73eff8804702c7ef86ddeebdb45 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 17:21:30 +0200 Subject: [PATCH 03/57] Moving dummycharacter collider to cursor pos in the editor (-> dropped items are positioned correctly) --- Subsurface/Source/Screens/EditMapScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index fb5bc8285..0d5bcd885 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -845,6 +845,7 @@ namespace Barotrauma { limb.body.SetTransform(mouseSimPos, 0.0f); } + dummyCharacter.AnimController.Collider.SetTransform(mouseSimPos, 0.0f); } dummyCharacter.ControlLocalPlayer((float)deltaTime, cam, false); From 8d1db582ad725577533f7a53c6ec6d98b7c26311 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 17:46:46 +0200 Subject: [PATCH 04/57] Fixed flickering lights --- Subsurface/Source/Items/Components/Power/PowerTransfer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs index d7257c601..dbdc7e932 100644 --- a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs @@ -54,10 +54,11 @@ namespace Barotrauma.Items.Components //by the constructions connected to the grid fullPower = 0.0f; fullLoad = 0.0f; - updateTimer = 0; + connectedList.Clear(); CheckJunctions(deltaTime); + updateTimer = 0; foreach (Powered p in connectedList) { From a18630c0b3d228154a757c140f9fb3f33c4d16ee Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 19:34:47 +0200 Subject: [PATCH 05/57] Fixed tutorial sub spawning twice --- Subsurface/Source/GameSession/GameSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index c743ef206..45d95adfc 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -159,7 +159,7 @@ namespace Barotrauma } } - public void StartShift(string levelSeed, bool loadSecondSub = true) + public void StartShift(string levelSeed, bool loadSecondSub = false) { Level level = Level.CreateRandom(levelSeed); From 8a8b9ca0fc45e5b0a6884baec9d458bf4266ffa7 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 19:44:19 +0200 Subject: [PATCH 06/57] Re-registering to master server if the server has timed out --- Subsurface/Source/Networking/GameServer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 933606a51..09c19dfcc 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -187,8 +187,8 @@ namespace Barotrauma.Networking { restClient = new RestClient(NetConfig.MasterServerUrl); } - - var request = new RestRequest("masterserver2.php", Method.GET); + + var request = new RestRequest("masterserver3.php", Method.GET); request.AddParameter("action", "addserver"); request.AddParameter("servername", name); request.AddParameter("serverport", Port); @@ -223,7 +223,7 @@ namespace Barotrauma.Networking restClient = new RestClient(NetConfig.MasterServerUrl); } - var request = new RestRequest("masterserver2.php", Method.GET); + var request = new RestRequest("masterserver3.php", Method.GET); request.AddParameter("action", "refreshserver"); request.AddParameter("gamestarted", gameStarted ? 1 : 0); request.AddParameter("currplayers", connectedClients.Count); @@ -263,6 +263,14 @@ namespace Barotrauma.Networking { masterServerResponded = true; + if (response.Content=="Error: server not found") + { + Log("Not registered to master server, re-registering...", Color.Red); + + RegisterToMasterServer(); + return; + } + if (response.ErrorException != null) { DebugConsole.NewMessage("Error while registering to master server (" + response.ErrorException + ")", Color.Red); From e6356a7516604494dae065020cd773978844e7c0 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 19:44:48 +0200 Subject: [PATCH 07/57] Updating wire sections when moving nodes in the editor --- Subsurface/Source/Items/Components/Signal/Wire.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index aad0c5a3a..fe70388d7 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -58,7 +58,6 @@ namespace Barotrauma.Items.Components public List Nodes; - private bool sectionsDirty; private List sections; Connection[] connections; @@ -463,6 +462,7 @@ namespace Barotrauma.Items.Components //if (item.Submarine != null) nodeWorldPos += item.Submarine.Position; Nodes[(int)selectedNodeIndex] = nodeWorldPos; + UpdateSections(); MapEntity.SelectEntity(item); } From b371e8d6007d86c98e3873101aaeabee5dbb9884 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 19:54:13 +0200 Subject: [PATCH 08/57] Made the tutorial a bit easier: the windows broken by the moloch leak much more slowly and pressure doesn't reach lethal levels in the command room --- .../GameModes/Tutorials/BasicTutorial.cs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Subsurface/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs b/Subsurface/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs index d48bb1f80..f4f5bfd89 100644 --- a/Subsurface/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs +++ b/Subsurface/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs @@ -289,7 +289,10 @@ namespace Barotrauma.Tutorials } yield return new WaitForSeconds(1.0f); - var moloch = Character.Create("Content/Characters/Moloch/moloch.xml", steering.Item.WorldPosition + Vector2.UnitX * 3000.0f); + var moloch = Character.Create( + "Content/Characters/Moloch/moloch.xml", + steering.Item.WorldPosition + new Vector2(3000.0f, -500.0f)); + moloch.PlaySound(AIController.AiState.Attack); yield return new WaitForSeconds(1.0f); @@ -304,21 +307,20 @@ namespace Barotrauma.Tutorials if (s.Rect.Right > steering.Item.CurrentHull.Rect.Right) windows.Add(s); } + float slowdownTimer = 1.0f; bool broken = false; do { - Submarine.MainSub.Velocity = Vector2.Zero; + steering.TargetVelocity = Vector2.Zero; + + slowdownTimer = Math.Max(0.0f, slowdownTimer - CoroutineManager.DeltaTime*0.3f); + Submarine.MainSub.Velocity *= slowdownTimer; moloch.AIController.SelectTarget(steering.Item.CurrentHull.AiTarget); Vector2 steeringDir = windows[0].WorldPosition - moloch.WorldPosition; if (steeringDir != Vector2.Zero) steeringDir = Vector2.Normalize(steeringDir); - //foreach (Limb limb in moloch.AnimController.Limbs) - //{ - // limb.body.LinearVelocity = new Vector2(limb.LinearVelocity.X*2.0f, limb.LinearVelocity.Y + steeringDir.Y*10.0f); - //} - - moloch.AIController.Steering = steeringDir; + moloch.AIController.SteeringManager.SteeringManual(CoroutineManager.DeltaTime, steeringDir*100.0f); foreach (Structure window in windows) { @@ -332,7 +334,7 @@ namespace Barotrauma.Tutorials } - yield return new WaitForSeconds(0.1f); + yield return CoroutineStatus.Running; } while (!broken); //fix everything except the command windows @@ -346,13 +348,13 @@ namespace Barotrauma.Tutorials if (isWindow) { - w.AddDamage(i, -w.SectionDamage(i) * 0.2f); + //decrease window damage to slow down the leaking + w.AddDamage(i, -w.SectionDamage(i) * 0.495f); } else { w.AddDamage(i, -100000.0f); } - } } @@ -368,11 +370,16 @@ namespace Barotrauma.Tutorials Door commandDoor2 = Item.ItemList.Find(i => i.HasTag("commanddoor2")).GetComponent(); Door commandDoor3 = Item.ItemList.Find(i => i.HasTag("commanddoor3")).GetComponent(); - while (commandDoor1.IsOpen || (commandDoor2.IsOpen || commandDoor3.IsOpen)) + //wait until the player is out of the room and the doors are closed + while (Character.Controlled.WorldPosition.X > commandDoor1.Item.WorldPosition.X || + (commandDoor1.IsOpen || (commandDoor2.IsOpen || commandDoor3.IsOpen))) { + //prevent the hull from filling up completely and crushing the player + steering.Item.CurrentHull.Volume = Math.Min(steering.Item.CurrentHull.Volume, steering.Item.CurrentHull.FullVolume * 0.9f); yield return CoroutineStatus.Running; } + infoBox = CreateInfoFrame("You should quickly find yourself a diving mask or a diving suit. " + "There are some in the room next to the airlock."); @@ -452,7 +459,7 @@ namespace Barotrauma.Tutorials yield return CoroutineStatus.Running; } - moloch.AnimController.SetPosition(ConvertUnits.ToSimUnits(Character.Controlled.WorldPosition + Vector2.UnitY * 1000.0f)); + moloch.AnimController.SetPosition(ConvertUnits.ToSimUnits(Character.Controlled.WorldPosition + Vector2.UnitY * 600.0f)); infoBox = CreateInfoFrame("Now we're ready to shoot! Select the railgun controller."); @@ -466,6 +473,7 @@ namespace Barotrauma.Tutorials while (!moloch.IsDead) { + moloch.AIController.SelectTarget(Character.Controlled.AiTarget); yield return CoroutineStatus.Running; } From b4515367f240c7d795563abf858d0d77fae91c93 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 10 Nov 2016 21:02:35 +0200 Subject: [PATCH 09/57] v0.5.3.4 --- Subsurface/Properties/AssemblyInfo.cs | 4 ++-- Subsurface/changelog.txt | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs index c7e4b6961..eace486ef 100644 --- a/Subsurface/Properties/AssemblyInfo.cs +++ b/Subsurface/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.5.3.3")] -[assembly: AssemblyFileVersion("0.5.3.3")] +[assembly: AssemblyVersion("0.5.3.4")] +[assembly: AssemblyFileVersion("0.5.3.4")] diff --git a/Subsurface/changelog.txt b/Subsurface/changelog.txt index c010737b6..b308dafde 100644 --- a/Subsurface/changelog.txt +++ b/Subsurface/changelog.txt @@ -1,3 +1,13 @@ +--------------------------------------------------------------------------------------------------------- +v0.5.3.4 +--------------------------------------------------------------------------------------------------------- + +- fixed screen turning black in the tutorial +- the moloch attack in the tutorial is now much easier to survive +- servers readd themselves to the master server if they have been removed from the server list +- fixed a bug that caused lights to flicker even if there's enough power +- fixed items disappearing when dropping them in the submarine editor + --------------------------------------------------------------------------------------------------------- v0.5.3.3 --------------------------------------------------------------------------------------------------------- From 0e9c20c6666a34a3a304075a68cea14ff385fe1b Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Thu, 10 Nov 2016 21:45:59 -0300 Subject: [PATCH 10/57] Case-sensitivity checks on Windows Should prevent "TigerThresher" from happening again. --- Subsurface/Source/Characters/CharacterInfo.cs | 1 + Subsurface/Source/DebugConsole.cs | 2 + Subsurface/Source/GUI/GUIStyle.cs | 6 ++- Subsurface/Source/Map/Submarine.cs | 1 + Subsurface/Source/Utils/ToolBox.cs | 44 +++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Subsurface/Source/Characters/CharacterInfo.cs b/Subsurface/Source/Characters/CharacterInfo.cs index 39cdcdf3b..a789df893 100644 --- a/Subsurface/Source/Characters/CharacterInfo.cs +++ b/Subsurface/Source/Characters/CharacterInfo.cs @@ -184,6 +184,7 @@ namespace Barotrauma spritePath = spritePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : ""); spritePath = spritePath.Replace("[HEADID]", HeadSpriteId.ToString()); + ToolBox.IsProperFilenameCase(spritePath); string fileName = Path.GetFileNameWithoutExtension(spritePath); //go through the files in the directory to find a matching sprite diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 16083a694..42554ccd6 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -748,6 +748,8 @@ namespace Barotrauma DebugConsole.ThrowError("TutorialSub.sub not found!"); } + break; + case "testasd": break; default: NewMessage("Command not found", Color.Red); diff --git a/Subsurface/Source/GUI/GUIStyle.cs b/Subsurface/Source/GUI/GUIStyle.cs index ea137a4ab..5fdc59c7d 100644 --- a/Subsurface/Source/GUI/GUIStyle.cs +++ b/Subsurface/Source/GUI/GUIStyle.cs @@ -14,7 +14,11 @@ namespace Barotrauma componentStyles = new Dictionary(); XDocument doc; - try { doc = XDocument.Load(file); } + try + { + ToolBox.IsProperFilenameCase(file); + doc = XDocument.Load(file); + } catch (Exception e) { DebugConsole.ThrowError("Loading style \"" + file + "\" failed", e); diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index de045b928..bf2ba494f 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -973,6 +973,7 @@ namespace Barotrauma { try { + ToolBox.IsProperFilenameCase(file); doc = XDocument.Load(file); } diff --git a/Subsurface/Source/Utils/ToolBox.cs b/Subsurface/Source/Utils/ToolBox.cs index 30cffdc35..101f2dd00 100644 --- a/Subsurface/Source/Utils/ToolBox.cs +++ b/Subsurface/Source/Utils/ToolBox.cs @@ -27,11 +27,55 @@ namespace Barotrauma public static class ToolBox { + public static bool IsProperFilenameCase(string filename) + { + char[] delimiters = { '/','\\' }; + string[] subDirs = filename.Split(delimiters); + string originalFilename = filename; + filename = ""; + + for (int i=0;i s.Equals(filename + subDirs[i + 1], StringComparison.Ordinal))) + { + return true; + } + else if (filePaths.Any(s => s.Equals(filename + subDirs[i + 1], StringComparison.OrdinalIgnoreCase))) + { + DebugConsole.ThrowError(originalFilename + " has incorrect case!"); + return false; + } + } + + string[] dirPaths = Directory.GetDirectories(filename); + + if (!dirPaths.Any(s => s.Equals(filename+subDirs[i+1],StringComparison.Ordinal))) + { + if (dirPaths.Any(s => s.Equals(filename + subDirs[i + 1], StringComparison.OrdinalIgnoreCase))) + { + DebugConsole.ThrowError(originalFilename + " has incorrect case!"); + } + else + { + DebugConsole.ThrowError(originalFilename + " doesn't exist!"); + } + return false; + } + } + return true; + } + public static XDocument TryLoadXml(string filePath) { XDocument doc; try { + IsProperFilenameCase(filePath); doc = XDocument.Load(filePath); } catch (Exception e) From 62d79aff036dfb63fc1f88c62174bbde7b689cbc Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Thu, 10 Nov 2016 21:48:50 -0300 Subject: [PATCH 11/57] Removed testasd command --- Subsurface/Source/DebugConsole.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 42554ccd6..16083a694 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -748,8 +748,6 @@ namespace Barotrauma DebugConsole.ThrowError("TutorialSub.sub not found!"); } - break; - case "testasd": break; default: NewMessage("Command not found", Color.Red); From 392bc132586d9b012272a16a7529a885c40024a7 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Thu, 10 Nov 2016 21:58:35 -0300 Subject: [PATCH 12/57] Removed case check from CharacterInfo because it corrects itself Just make sure to not get the directory casing wrong and we'll be good --- Subsurface/Source/Characters/CharacterInfo.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Subsurface/Source/Characters/CharacterInfo.cs b/Subsurface/Source/Characters/CharacterInfo.cs index a789df893..4eada1df1 100644 --- a/Subsurface/Source/Characters/CharacterInfo.cs +++ b/Subsurface/Source/Characters/CharacterInfo.cs @@ -183,8 +183,7 @@ namespace Barotrauma spritePath = spritePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : ""); spritePath = spritePath.Replace("[HEADID]", HeadSpriteId.ToString()); - - ToolBox.IsProperFilenameCase(spritePath); + string fileName = Path.GetFileNameWithoutExtension(spritePath); //go through the files in the directory to find a matching sprite From 96cedd67f133e365f072c7c7d173f8fef1db9962 Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 11 Nov 2016 17:34:21 +0200 Subject: [PATCH 13/57] ItemComponents that don't implement the IDrawableComponent interface can't be added to the list of drawable components, pickTimer is ignored when deattaching items from the walls in the editor --- .../Source/Items/Components/Holdable/Pickable.cs | 7 +------ .../Source/Items/Components/ItemComponent.cs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Subsurface/Source/Items/Components/Holdable/Pickable.cs b/Subsurface/Source/Items/Components/Holdable/Pickable.cs index a4733defc..b14a2da64 100644 --- a/Subsurface/Source/Items/Components/Holdable/Pickable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Pickable.cs @@ -112,10 +112,8 @@ namespace Barotrauma.Items.Components var leftHand = picker.AnimController.GetLimb(LimbType.LeftHand); var rightHand = picker.AnimController.GetLimb(LimbType.RightHand); - Drawable = true; - pickTimer = 0.0f; - while (pickTimer < requiredTime) + while (pickTimer < requiredTime && Screen.Selected != GameMain.EditMapScreen) { if (picker.IsKeyDown(InputType.Aim) || !item.IsInPickRange(picker.WorldPosition) || @@ -125,7 +123,6 @@ namespace Barotrauma.Items.Components yield return CoroutineStatus.Success; } - picker.UpdateHUDProgressBar( this, item.WorldPosition, @@ -158,8 +155,6 @@ namespace Barotrauma.Items.Components private void StopPicking(Character picker) { - Drawable = false; - picker.AnimController.Anim = AnimController.Animation.None; pickTimer = 0.0f; } diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index f8250618a..1a811bf91 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -117,17 +117,22 @@ namespace Barotrauma.Items.Components set { if (value == drawable) return; + if (!(this is IDrawableComponent)) + { + DebugConsole.ThrowError("Couldn't make ''"+this+"'' drawable (the component doesn't implement the IDrawableComponent interface)"); + return; + } + drawable = value; if (drawable) { - if (!item.drawableComponents.Contains(this as IDrawableComponent)) - item.drawableComponents.Add(this as IDrawableComponent); + if (!item.drawableComponents.Contains((IDrawableComponent)this)) + item.drawableComponents.Add((IDrawableComponent)this); } else { - item.drawableComponents.Remove(this as IDrawableComponent); - } - + item.drawableComponents.Remove((IDrawableComponent)this); + } } } From e6b291987747da6408c66b484680d7b4879e9c7e Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 11 Nov 2016 17:37:03 +0200 Subject: [PATCH 14/57] Items/structures can be copied by holding ctrl in the editor --- Subsurface/Source/Items/Item.cs | 22 +++++++++ Subsurface/Source/Map/MapEntity.cs | 52 +++++++++++++++++----- Subsurface/Source/Map/Structure.cs | 6 ++- Subsurface/Source/Screens/EditMapScreen.cs | 12 ++--- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 75a051450..9e226240f 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -413,6 +413,28 @@ namespace Barotrauma ItemList.Add(this); } + public override MapEntity Clone() + { + Item clone = new Item(rect, prefab, Submarine); + + foreach (KeyValuePair property in properties) + { + if (!property.Value.Attributes.OfType().Any()) continue; + clone.properties[property.Key].TrySetValue(property.Value.GetValue()); + } + + if (ContainedItems!=null) + { + foreach (Item containedItem in ContainedItems) + { + var containedClone = containedItem.Clone(); + clone.ownInventory.TryPutItem(containedItem); + } + } + + return clone; + } + public T GetComponent() { foreach (ItemComponent ic in components) diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 4a614542e..789581e0b 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -10,7 +10,7 @@ using System.Collections.ObjectModel; namespace Barotrauma { - class MapEntity : Entity + abstract class MapEntity : Entity { public static List mapEntityList = new List(); @@ -149,7 +149,11 @@ namespace Barotrauma if (aiTarget == null) return 0.0f; return aiTarget.SoundRange; } - set { aiTarget.SoundRange = value; } + set + { + if (aiTarget == null) return; + aiTarget.SoundRange = value; + } } public float SightRange @@ -202,6 +206,22 @@ namespace Barotrauma { return (Submarine.RectContains(WorldRect, position)); } + + public virtual MapEntity Clone() + { + return null; + } + + public static List Clone(List entitiesToClone) + { + List clones = new List(); + foreach (MapEntity e in entitiesToClone) + { + clones.Add(e.Clone()); + } + + return clones; + } protected void InsertToList() { @@ -341,22 +361,30 @@ namespace Barotrauma } //started moving selected entities - if (startMovingPos != Vector2.Zero) - { - if (PlayerInput.LeftButtonReleased()) + if (startMovingPos != Vector2.Zero && PlayerInput.LeftButtonReleased()) + { + //mouse released -> move the entities to the new position of the mouse + + Vector2 moveAmount = position - startMovingPos; + moveAmount = Submarine.VectorToWorldGrid(moveAmount); + + if (moveAmount != Vector2.Zero) { - //mouse released -> move the entities to the new position of the mouse + //clone + if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) + { + var clones = Clone(selectedList); + clones.ForEach(c => c.Move(moveAmount)); - Vector2 moveAmount = position - startMovingPos; - moveAmount = Submarine.VectorToWorldGrid(moveAmount); - - if (moveAmount != Vector2.Zero) + selectedList = clones; + } + else // move { foreach (MapEntity e in selectedList) e.Move(moveAmount); } - - startMovingPos = Vector2.Zero; } + + startMovingPos = Vector2.Zero; } //started dragging a "selection rectangle" else if (selectionPos != Vector2.Zero) diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 12710e677..a9fbc4022 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -256,7 +256,6 @@ namespace Barotrauma } } - if (prefab.CastShadow) { GenerateConvexHull(); @@ -265,6 +264,11 @@ namespace Barotrauma InsertToList(); } + public override MapEntity Clone() + { + return new Structure(rect, prefab, Submarine); + } + private void CreateStairBodies() { bodies = new List(); diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 0d5bcd885..f8e2e9e8a 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -816,13 +816,7 @@ namespace Barotrauma if (GUIComponent.MouseOn == null) { - //if (nameBox.Selected && PlayerInput.LeftButtonClicked()) - //{ - // ChangeSubName(nameBox, nameBox.Text); - //} - cam.MoveCamera((float)deltaTime); - //cam.Zoom = MathHelper.Clamp(cam.Zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f)*cam.Zoom, 0.1f, 2.0f); } if (characterMode || wiringMode) @@ -854,7 +848,6 @@ namespace Barotrauma dummyCharacter.Submarine = Submarine.MainSub; cam.TargetPos = Vector2.Zero; - } } else @@ -869,7 +862,7 @@ namespace Barotrauma { if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(cam); - MapEntity.UpdateEditor(cam); + MapEntity.UpdateEditor(cam); } leftPanel.Update((float)deltaTime); @@ -907,9 +900,10 @@ namespace Barotrauma foreach (Item item in dummyCharacter.SelectedItems) { if (item == null) continue; - item.SetTransform(dummyCharacter.SimPosition, 0.0f); + item.SetTransform(dummyCharacter.SimPosition, 0.0f); item.Update(cam, (float)deltaTime); + item.SetTransform(item.body.SimPosition, 0.0f); } if (dummyCharacter.SelectedConstruction != null) From 0aa4b7a93de422f6d5a9275d2b15a4014ec2c774 Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 11 Nov 2016 17:58:14 +0200 Subject: [PATCH 15/57] Copy, paste & cut functionality in the editor --- Subsurface/Source/Map/MapEntity.cs | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 789581e0b..3f64cedad 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Xml.Linq; using FarseerPhysics; using Microsoft.Xna.Framework; @@ -16,6 +17,7 @@ namespace Barotrauma //which entities have been selected for editing public static List selectedList = new List(); + private static List copiedList = new List(); protected static GUIComponent editingHUD; @@ -329,10 +331,40 @@ namespace Barotrauma if (PlayerInput.KeyDown(Keys.Delete)) { - foreach (MapEntity e in selectedList) e.Remove(); + selectedList.ForEach(e => e.Remove()); selectedList.Clear(); } + if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) + { + if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.C) && + PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.C)) + { + copiedList = new List(selectedList); + } + else if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.X) && + PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.X)) + { + copiedList = new List(selectedList); + selectedList.ForEach(e => e.Remove()); + selectedList.Clear(); + } + else if (copiedList.Count > 0 && + PlayerInput.GetKeyboardState.IsKeyDown(Keys.V) && + PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.V)) + { + var clones = Clone(copiedList); + + Vector2 center = Vector2.Zero; + clones.ForEach(c => center += c.WorldPosition); + center /= clones.Count; + + clones.ForEach(c => c.Move(cam.WorldViewCenter - center)); + + selectedList = new List(clones); + } + } + Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); MapEntity highLightedEntity = null; From d403b38440a74b028ae8b8a7e574fcc1cfd2b5ee Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 12 Nov 2016 15:44:02 +0200 Subject: [PATCH 16/57] Hull, Gap & WayPoint cloning, equipped items are removed when switching from character mode to wiring or mode or vice versa --- Subsurface/Source/Map/Gap.cs | 7 ++++++- Subsurface/Source/Map/Hull.cs | 5 +++++ Subsurface/Source/Map/MapEntity.cs | 2 +- Subsurface/Source/Map/WayPoint.cs | 10 ++++++++++ Subsurface/Source/Screens/EditMapScreen.cs | 4 ++-- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 5790aec50..3d346aaa6 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -114,6 +114,11 @@ namespace Barotrauma InsertToList(); } + public override MapEntity Clone() + { + return new Gap(rect, isHorizontal, Submarine); + } + public override void Move(Vector2 amount) { base.Move(amount); @@ -210,7 +215,7 @@ namespace Barotrauma isHorizontal ? new Vector2(rect.Height / 16.0f, 1.0f) : new Vector2(rect.Width / 16.0f, 1.0f)); } - if (isSelected) + if (IsSelected) { GUI.DrawRectangle(sb, new Vector2(WorldRect.X - 5, -WorldRect.Y - 5), diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index bd63390df..784c8359d 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -260,6 +260,11 @@ namespace Barotrauma return rect; } + + public override MapEntity Clone() + { + return new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), rect, Submarine); + } public static EntityGrid GenerateEntityGrid(Submarine submarine) { diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 3f64cedad..5cc7560ae 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -211,7 +211,7 @@ namespace Barotrauma public virtual MapEntity Clone() { - return null; + throw new NotImplementedException(); } public static List Clone(List entitiesToClone) diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index dba83d407..665ecf948 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -113,6 +113,16 @@ namespace Barotrauma currentHull = Hull.FindHull(WorldPosition); } + public override MapEntity Clone() + { + var clone = new WayPoint(rect, Submarine); + clone.idCardTags = idCardTags; + clone.spawnType = spawnType; + clone.assignedJob = assignedJob; + + return clone; + } + public override bool IsMouseOn(Vector2 position) { if (IsHidden()) return false; diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index f8e2e9e8a..45e6f5229 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -298,7 +298,7 @@ namespace Barotrauma private void CreateDummyCharacter() { - if (dummyCharacter != null) dummyCharacter.Remove(); + if (dummyCharacter != null) RemoveDummyCharacter(); dummyCharacter = Character.Create(Character.HumanConfigFile, Vector2.Zero); @@ -588,7 +588,7 @@ namespace Barotrauma characterMode = !characterMode; //button.Color = (characterMode) ? Color.Gold : Color.White; - wiringMode = false; + wiringMode = false; if (characterMode) { From 7fa660d38e17256b545e436fdea08b1d0b38c22c Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 12 Nov 2016 15:51:46 +0200 Subject: [PATCH 17/57] Wires can be cloned and moved if both items it's connected to are selected --- .../Source/Items/Components/Signal/Wire.cs | 28 +++++- Subsurface/Source/Items/Item.cs | 17 +++- Subsurface/Source/Map/Hull.cs | 2 +- Subsurface/Source/Map/LinkedSubmarine.cs | 2 +- Subsurface/Source/Map/MapEntity.cs | 90 +++++++++++++------ Subsurface/Source/Map/Structure.cs | 2 +- Subsurface/Source/Map/Submarine.cs | 8 +- Subsurface/Source/Map/WayPoint.cs | 2 +- Subsurface/Source/Screens/EditMapScreen.cs | 1 - 9 files changed, 107 insertions(+), 45 deletions(-) diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index fe70388d7..7fe3c3529 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -267,7 +267,7 @@ namespace Barotrauma.Items.Components item.NewComponentEvent(this, true, true); } - Drawable = Nodes.Any(); + Drawable = sections.Count > 0; } public override bool Pick(Character picker) @@ -277,6 +277,20 @@ namespace Barotrauma.Items.Components return true; } + public override void Move(Vector2 amount) + { + if (item.IsSelected) MoveNodes(amount); + } + + public void MoveNodes(Vector2 amount) + { + for (int i = 0; i < Nodes.Count; i++) + { + Nodes[i] += amount; + } + UpdateSections(); + } + public void UpdateSections() { sections.Clear(); @@ -285,6 +299,7 @@ namespace Barotrauma.Items.Components { sections.Add(new WireSection(Nodes[i], Nodes[i + 1])); } + Drawable = sections.Count > 0; } private void ClearConnections() @@ -303,7 +318,7 @@ namespace Barotrauma.Items.Components connections[i] = null; } - Drawable = false; + Drawable = sections.Count > 0; } private Vector2 RoundNode(Vector2 position, Hull hull) @@ -365,7 +380,7 @@ namespace Barotrauma.Items.Components public void Draw(SpriteBatch spriteBatch, bool editing) { - if (!Nodes.Any()) + if (sections.Count == 0) { Drawable = false; return; @@ -386,6 +401,13 @@ namespace Barotrauma.Items.Components section.Draw(spriteBatch, Color.Gold, drawOffset, depth, 0.5f); } } + else if (item.IsSelected) + { + foreach (WireSection section in sections) + { + section.Draw(spriteBatch, Color.Red, drawOffset, depth, 0.5f); + } + } foreach (WireSection section in sections) { diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 9e226240f..aeecfc1f3 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -423,12 +423,21 @@ namespace Barotrauma clone.properties[property.Key].TrySetValue(property.Value.GetValue()); } - if (ContainedItems!=null) + for (int i = 0; i < components.Count; i++) + { + foreach (KeyValuePair property in components[i].properties) + { + if (!property.Value.Attributes.OfType().Any()) continue; + clone.components[i].properties[property.Key].TrySetValue(property.Value.GetValue()); + } + } + + if (ContainedItems != null) { foreach (Item containedItem in ContainedItems) { var containedClone = containedItem.Clone(); - clone.ownInventory.TryPutItem(containedItem); + clone.ownInventory.TryPutItem(containedClone as Item); } } @@ -880,7 +889,7 @@ namespace Barotrauma public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { if (!Visible) return; - Color color = (isSelected && editing) ? color = Color.Red : spriteColor; + Color color = (IsSelected && editing) ? color = Color.Red : spriteColor; if (isHighlighted) color = Color.Orange; SpriteEffects oldEffects = prefab.sprite.effects; @@ -943,7 +952,7 @@ namespace Barotrauma return; } - if (isSelected || isHighlighted) + if (IsSelected || isHighlighted) { GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y+rect.Height/2)), new Vector2(rect.Width, rect.Height), Color.Green,false,0,(int)Math.Max((1.5f/GameScreen.Selected.Cam.Zoom),1.0f)); diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 784c8359d..1d7a76708 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -609,7 +609,7 @@ namespace Barotrauma } - if ((isSelected || isHighlighted) && editing) + if ((IsSelected || isHighlighted) && editing) { GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X + 5, -drawRect.Y + 5), diff --git a/Subsurface/Source/Map/LinkedSubmarine.cs b/Subsurface/Source/Map/LinkedSubmarine.cs index b17c76116..856a65f65 100644 --- a/Subsurface/Source/Map/LinkedSubmarine.cs +++ b/Subsurface/Source/Map/LinkedSubmarine.cs @@ -111,7 +111,7 @@ namespace Barotrauma if (!editing || wallVertices == null) return; Color color = (isHighlighted) ? Color.Orange : Color.Green; - if (isSelected) color = Color.Red; + if (IsSelected) color = Color.Red; Vector2 pos = Position; diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 5cc7560ae..69f2475b5 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -8,6 +8,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System.Collections.ObjectModel; +using Barotrauma.Items.Components; namespace Barotrauma { @@ -16,7 +17,7 @@ namespace Barotrauma public static List mapEntityList = new List(); //which entities have been selected for editing - public static List selectedList = new List(); + private static List selectedList = new List(); private static List copiedList = new List(); protected static GUIComponent editingHUD; @@ -39,7 +40,7 @@ namespace Barotrauma //is the mouse inside the rect protected bool isHighlighted; - protected bool isSelected; + //protected bool isSelected; private static bool disableSelect; public static bool DisableSelect @@ -175,8 +176,7 @@ namespace Barotrauma public bool IsSelected { - get { return isSelected; } - set { isSelected = value; } + get { return selectedList.Contains(this); } } protected bool ResizeHorizontal @@ -219,7 +219,43 @@ namespace Barotrauma List clones = new List(); foreach (MapEntity e in entitiesToClone) { + Debug.Assert(e != null); clones.Add(e.Clone()); + Debug.Assert(clones.Last() != null); + } + + Debug.Assert(clones.Count == entitiesToClone.Count); + + //connect clone wires to the clone items + for (int i = 0; i < clones.Count; i++) + { + var cloneItem = clones[i] as Item; + if (cloneItem == null) continue; + + var cloneWire = cloneItem.GetComponent(); + if (cloneWire == null) continue; + + var originalWire = ((Item)entitiesToClone[i]).GetComponent(); + + cloneWire.Nodes = new List(originalWire.Nodes); + cloneWire.UpdateSections(); + + for (int n = 0; n < 2; n++) + { + if (originalWire.Connections[n] == null) continue; + + var connectedItem = originalWire.Connections[n].Item; + if (connectedItem == null) continue; + + //index of the item the wire is connected to + int itemIndex = entitiesToClone.IndexOf(connectedItem); + //index of the connection in the connectionpanel of the target item + int connectionIndex = connectedItem.Connections.IndexOf(originalWire.Connections[n]); + + (clones[itemIndex] as Item).GetComponent().Connections[connectionIndex].TryAddLink(cloneWire); + cloneWire.Connect((clones[itemIndex] as Item).Connections[connectionIndex], false); + + } } return clones; @@ -311,7 +347,6 @@ namespace Barotrauma foreach (MapEntity e in mapEntityList) { e.isHighlighted = false; - e.isSelected = false; } if (DisableSelect) @@ -359,9 +394,8 @@ namespace Barotrauma clones.ForEach(c => center += c.WorldPosition); center /= clones.Count; - clones.ForEach(c => c.Move(cam.WorldViewCenter - center)); - selectedList = new List(clones); + selectedList.ForEach(c => c.Move(cam.WorldViewCenter - center)); } } @@ -380,18 +414,12 @@ namespace Barotrauma { if (e.IsMouseOn(position)) highLightedEntity = e; } - e.isSelected = false; } if (highLightedEntity != null) highLightedEntity.isHighlighted = true; } - - foreach (MapEntity e in selectedList) - { - e.isSelected = true; - } - + //started moving selected entities if (startMovingPos != Vector2.Zero && PlayerInput.LeftButtonReleased()) { @@ -406,9 +434,8 @@ namespace Barotrauma if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) { var clones = Clone(selectedList); - clones.ForEach(c => c.Move(moveAmount)); - selectedList = clones; + selectedList.ForEach(c => c.Move(moveAmount)); } else // move { @@ -463,6 +490,25 @@ namespace Barotrauma { selectedList = newSelection; } + + //select wire if both items it's connected to are selected + var selectedItems = selectedList.Where(e => e is Item).Cast().ToList(); + foreach (Item item in selectedItems) + { + if (item.Connections == null) continue; + foreach (Connection c in item.Connections) + { + foreach (Wire w in c.Wires) + { + if (w == null || selectedList.Contains(w.Item)) continue; + + if (w.OtherConnection(c) != null && selectedList.Contains(w.OtherConnection(c).Item)) + { + selectedList.Add(w.Item); + } + } + } + } selectionPos = Vector2.Zero; selectionSize = Vector2.Zero; @@ -471,7 +517,6 @@ namespace Barotrauma //default, not doing anything specific yet else { - if (PlayerInput.LeftButtonHeld() && PlayerInput.KeyUp(Keys.Space)) { @@ -483,12 +528,10 @@ namespace Barotrauma selectionPos = position; } - - - } - + } } + /// /// Draw the "selection rectangle" and outlines of entities that are being dragged (if any) /// @@ -565,10 +608,6 @@ namespace Barotrauma public static void DeselectAll() { - foreach (MapEntity e in selectedList) - { - e.isSelected = false; - } selectedList.Clear(); } @@ -577,7 +616,6 @@ namespace Barotrauma { DeselectAll(); - entity.isSelected = true; selectedList.Add(entity); } diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index a9fbc4022..966bff8c9 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -495,7 +495,7 @@ namespace Barotrauma if (prefab.sprite == null) return; Color color = (isHighlighted) ? Color.Orange : Color.White; - if (isSelected && editing) + if (IsSelected && editing) { color = Color.Red; diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index bf2ba494f..28018729f 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -1080,13 +1080,7 @@ namespace Barotrauma if (item.Submarine != this) continue; var wire = item.GetComponent(); - if (wire == null) continue; - - for (int i = 0; i < wire.Nodes.Count; i++) - { - wire.Nodes[i] -= center; - } - wire.UpdateSections(); + if (wire != null) wire.MoveNodes(-center); } for (int i = 0; i < MapEntity.mapEntityList.Count; i++) diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index 665ecf948..38da4ef63 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -144,7 +144,7 @@ namespace Barotrauma drawPos.Y = -drawPos.Y; Color clr = currentHull == null ? Color.Blue : Color.White; - if (isSelected) clr = Color.Red; + if (IsSelected) clr = Color.Red; if (isHighlighted) clr = Color.DarkRed; int iconX = iconIndices[(int)spawnType]*IconSize % iconTexture.Width; diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 45e6f5229..9d5544727 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -602,7 +602,6 @@ namespace Barotrauma foreach (MapEntity me in MapEntity.mapEntityList) { me.IsHighlighted = false; - me.IsSelected = false; } return true; From 0353732e7eb8c4d0a2d45b0ba307dd213c00c139 Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 12 Nov 2016 18:26:47 +0200 Subject: [PATCH 18/57] Fixed highlighting items when highlighting a wire in a connection panel --- .../Items/Components/Signal/Connection.cs | 5 ++-- .../Components/Signal/ConnectionPanel.cs | 26 +++++++++++++++---- .../Source/Items/Components/Signal/Wire.cs | 4 +-- Subsurface/Source/Items/Item.cs | 2 -- Subsurface/Source/Screens/GameScreen.cs | 5 ++++ 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Subsurface/Source/Items/Components/Signal/Connection.cs b/Subsurface/Source/Items/Components/Signal/Connection.cs index 08b461a23..4c122bd96 100644 --- a/Subsurface/Source/Items/Components/Signal/Connection.cs +++ b/Subsurface/Source/Items/Components/Signal/Connection.cs @@ -24,7 +24,7 @@ namespace Barotrauma.Items.Components private Item item; public readonly bool IsOutput; - + private static Wire draggingConnected; private List effects; @@ -423,8 +423,7 @@ namespace Barotrauma.Items.Components { if (mouseOn) { - item.IsHighlighted = true; - wire.Item.IsHighlighted = true; + ConnectionPanel.HighlightedWire = wire; if (!wire.Locked) { diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index 43a8be392..b9d1fd632 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -7,7 +7,9 @@ using System.Xml.Linq; namespace Barotrauma.Items.Components { class ConnectionPanel : ItemComponent - { + { + public static Wire HighlightedWire; + public List Connections; Character user; @@ -33,15 +35,29 @@ namespace Barotrauma.Items.Components IsActive = true; } - public override void DrawHUD(SpriteBatch spriteBatch, Character character) + public override void UpdateHUD(Character character) { if (character != Character.Controlled || character != user) return; if (Screen.Selected != GameMain.EditMapScreen && - character.IsKeyHit(InputType.Select) && - character.SelectedConstruction==this.item) character.SelectedConstruction = null; - + character.IsKeyHit(InputType.Select) && + character.SelectedConstruction == this.item) character.SelectedConstruction = null; + + if (HighlightedWire != null) + { + HighlightedWire.Item.IsHighlighted = true; + if (HighlightedWire.Connections[0].Item != null) HighlightedWire.Connections[0].Item.IsHighlighted = true; + if (HighlightedWire.Connections[1].Item != null) HighlightedWire.Connections[1].Item.IsHighlighted = true; + } + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + if (character != Character.Controlled || character != user) return; + + HighlightedWire = null; Connection.DrawConnections(spriteBatch, this, character); + } public override XElement Save(XElement parentElement) diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index 7fe3c3529..b505011ca 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -398,14 +398,14 @@ namespace Barotrauma.Items.Components { foreach (WireSection section in sections) { - section.Draw(spriteBatch, Color.Gold, drawOffset, depth, 0.5f); + section.Draw(spriteBatch, Color.Gold, drawOffset, depth + 0.00001f, 0.7f); } } else if (item.IsSelected) { foreach (WireSection section in sections) { - section.Draw(spriteBatch, Color.Red, drawOffset, depth, 0.5f); + section.Draw(spriteBatch, Color.Red, drawOffset, depth + 0.00001f, 0.7f); } } diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index aeecfc1f3..1cbe1e4ee 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -768,8 +768,6 @@ namespace Barotrauma inWater = IsInWater(); if (inWater) ApplyStatusEffects(ActionType.InWater, deltaTime); - isHighlighted = false; - if (body == null || !body.Enabled) return; if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index d01389cf1..23083c0f2 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -115,6 +115,11 @@ namespace Barotrauma } #endif + foreach (MapEntity e in MapEntity.mapEntityList) + { + e.IsHighlighted = false; + } + if (GameMain.GameSession != null) GameMain.GameSession.Update((float)deltaTime); if (Level.Loaded != null) Level.Loaded.Update((float)deltaTime); From c2098f06f85068d418472359308f746341c98716 Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 12 Nov 2016 18:29:39 +0200 Subject: [PATCH 19/57] Removed a redundant DrawRectangle method & redundant sub visibility culling in GameScreen --- Subsurface/Source/GUI/GUI.cs | 35 ++++++++----------- .../Items/Components/Machines/MiniMap.cs | 2 +- Subsurface/Source/Map/Map/Map.cs | 2 +- Subsurface/Source/Map/Submarine.cs | 2 +- Subsurface/Source/Screens/GameScreen.cs | 16 --------- 5 files changed, 17 insertions(+), 40 deletions(-) diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index 8fccc7e72..2591365e3 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -272,16 +272,6 @@ namespace Barotrauma } } - public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, int thickness, float depth = 0.0f) - { - DrawLine(sb, new Vector2(rect.X, rect.Y), new Vector2(rect.Right, rect.Y), clr, depth, thickness); - DrawLine(sb, new Vector2(rect.X, rect.Bottom-thickness), new Vector2(rect.Right, rect.Bottom-thickness), clr, depth, thickness); - - DrawLine(sb, new Vector2(rect.X+thickness, rect.Y+thickness), new Vector2(rect.X+thickness, rect.Bottom-thickness), clr, depth, thickness); - - DrawLine(sb, new Vector2(rect.Right, rect.Y + thickness), new Vector2(rect.Right, rect.Bottom - thickness), clr, depth, thickness); - } - public static void DrawProgressBar(SpriteBatch sb, Vector2 start, Vector2 size, float progress, Color clr, float depth = 0.0f) { DrawProgressBar(sb, start, size, progress, clr, new Color(0.5f, 0.57f, 0.6f, 1.0f), depth); @@ -428,27 +418,30 @@ namespace Barotrauma if (GameMain.DebugDraw) { - spriteBatch.DrawString(SmallFont, + DrawString(spriteBatch, new Vector2(10, 10), "FPS: " + (int)GameMain.FrameCounter.AverageFramesPerSecond, - new Vector2(10, 10), Color.White); + Color.White, Color.Black * 0.5f, 0, SmallFont); - spriteBatch.DrawString(SmallFont, + DrawString(spriteBatch, new Vector2(10, 20), "Physics: " + GameMain.World.UpdateTime, - new Vector2(10, 20), Color.White); + Color.White, Color.Black * 0.5f, 0, SmallFont); - spriteBatch.DrawString(SmallFont, + DrawString(spriteBatch, new Vector2(10, 30), "Bodies: " + GameMain.World.BodyList.Count + " (" + GameMain.World.BodyList.FindAll(b => b.Awake && b.Enabled).Count + " awake)", - new Vector2(10, 30), Color.White); + Color.White, Color.Black * 0.5f, 0, SmallFont); - spriteBatch.DrawString(SmallFont, - "Camera pos: " + GameMain.GameScreen.Cam.Position.ToPoint(), - new Vector2(10, 40), Color.White); + if (Screen.Selected.Cam != null) + { + DrawString(spriteBatch, new Vector2(10, 40), + "Camera pos: " + Screen.Selected.Cam.Position.ToPoint(), + Color.White, Color.Black * 0.5f, 0, SmallFont); + } if (Submarine.MainSub != null) { - spriteBatch.DrawString(SmallFont, + DrawString(spriteBatch, new Vector2(10, 50), "Sub pos: " + Submarine.MainSub.Position.ToPoint(), - new Vector2(10, 50), Color.White); + Color.White, Color.Black * 0.5f, 0, SmallFont); } for (int i = 1; i < Sounds.SoundManager.DefaultSourceCount; i++) diff --git a/Subsurface/Source/Items/Components/Machines/MiniMap.cs b/Subsurface/Source/Items/Components/Machines/MiniMap.cs index 784d44f0b..c98f1e07d 100644 --- a/Subsurface/Source/Items/Components/Machines/MiniMap.cs +++ b/Subsurface/Source/Items/Components/Machines/MiniMap.cs @@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components Color.Black * 0.5f, 2, GUI.SmallFont); } - GUI.DrawRectangle(spriteBatch, hullRect, borderColor, 2); + GUI.DrawRectangle(spriteBatch, hullRect, borderColor, false, 0.0f, 2); } } diff --git a/Subsurface/Source/Map/Map/Map.cs b/Subsurface/Source/Map/Map/Map.cs index 64eae3e71..f54bf631a 100644 --- a/Subsurface/Source/Map/Map/Map.cs +++ b/Subsurface/Source/Map/Map/Map.cs @@ -358,7 +358,7 @@ namespace Barotrauma } rect.Inflate(8, 8); - GUI.DrawRectangle(spriteBatch, rect, Color.Black, 8); + GUI.DrawRectangle(spriteBatch, rect, Color.Black, false, 0.0f, 8); GUI.DrawRectangle(spriteBatch, rect, Color.LightGray); for (int i = 0; i < locations.Count; i++) diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 28018729f..76f0db368 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -352,7 +352,7 @@ namespace Barotrauma worldBorders.Location += sub.WorldPosition.ToPoint(); worldBorders.Y = -worldBorders.Y; - GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, 5); + GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5); if (sub.subBody.MemPos.Count < 2) continue; diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index 23083c0f2..c77d130b3 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -227,22 +227,6 @@ namespace Barotrauma GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition); } - List visibleSubs = new List(); - foreach (Submarine sub in Submarine.Loaded) - { - Rectangle worldBorders = new Rectangle( - sub.Borders.X + (int)sub.WorldPosition.X - 500, - sub.Borders.Y + (int)sub.WorldPosition.Y + 500, - sub.Borders.Width + 1000, - sub.Borders.Height + 1000); - - - if (Submarine.RectsOverlap(worldBorders, cam.WorldView)) - { - visibleSubs.Add(sub); - } - } - //---------------------------------------------------------------------------------------- //1. draw the background, characters and the parts of the submarine that are behind them //---------------------------------------------------------------------------------------- From b86231170fafc2b946e2ba5f8e257f70f2d9e5f1 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 14 Nov 2016 16:29:15 +0200 Subject: [PATCH 20/57] Fixed selectionrect becoming active when moving entities in the editor, null check in ConnectionPanel.UpdateHUD --- .../Components/Signal/ConnectionPanel.cs | 4 +- Subsurface/Source/Map/MapEntity.cs | 44 ++++++++++--------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index b9d1fd632..a6dbf2f18 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -46,8 +46,8 @@ namespace Barotrauma.Items.Components if (HighlightedWire != null) { HighlightedWire.Item.IsHighlighted = true; - if (HighlightedWire.Connections[0].Item != null) HighlightedWire.Connections[0].Item.IsHighlighted = true; - if (HighlightedWire.Connections[1].Item != null) HighlightedWire.Connections[1].Item.IsHighlighted = true; + if (HighlightedWire.Connections[0] != null && HighlightedWire.Connections[0].Item != null) HighlightedWire.Connections[0].Item.IsHighlighted = true; + if (HighlightedWire.Connections[1] != null && HighlightedWire.Connections[1].Item != null) HighlightedWire.Connections[1].Item.IsHighlighted = true; } } diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 69f2475b5..288dff844 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -421,29 +421,33 @@ namespace Barotrauma } //started moving selected entities - if (startMovingPos != Vector2.Zero && PlayerInput.LeftButtonReleased()) + if (startMovingPos != Vector2.Zero) { - //mouse released -> move the entities to the new position of the mouse - - Vector2 moveAmount = position - startMovingPos; - moveAmount = Submarine.VectorToWorldGrid(moveAmount); - - if (moveAmount != Vector2.Zero) + if (PlayerInput.LeftButtonReleased()) { - //clone - if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) - { - var clones = Clone(selectedList); - selectedList = clones; - selectedList.ForEach(c => c.Move(moveAmount)); - } - else // move - { - foreach (MapEntity e in selectedList) e.Move(moveAmount); - } - } + //mouse released -> move the entities to the new position of the mouse - startMovingPos = Vector2.Zero; + Vector2 moveAmount = position - startMovingPos; + moveAmount = Submarine.VectorToWorldGrid(moveAmount); + + if (moveAmount != Vector2.Zero) + { + //clone + if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) + { + var clones = Clone(selectedList); + selectedList = clones; + selectedList.ForEach(c => c.Move(moveAmount)); + } + else // move + { + foreach (MapEntity e in selectedList) e.Move(moveAmount); + } + } + + startMovingPos = Vector2.Zero; + } + } //started dragging a "selection rectangle" else if (selectionPos != Vector2.Zero) From 335bf0089048e46b31cbf2d5267b104a8aea7663 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 14 Nov 2016 17:52:26 +0200 Subject: [PATCH 21/57] Entity removal fixes --- .../Source/Characters/Animation/Ragdoll.cs | 13 +++++++++- Subsurface/Source/Characters/Character.cs | 2 ++ Subsurface/Source/Characters/Limb.cs | 25 ++++++++++++++++--- Subsurface/Source/Map/Entity.cs | 9 ++++++- Subsurface/Source/Map/Hull.cs | 2 +- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index d28c46fea..050bf0efc 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -1155,11 +1155,22 @@ namespace Barotrauma public void Remove() { - foreach (Limb l in Limbs) l.Remove(); + foreach (Limb l in Limbs) + { + l.Remove(); + } + Limbs = null; + + collider.Remove(); + collider = null; + foreach (RevoluteJoint joint in limbJoints) { GameMain.World.RemoveJoint(joint); } + limbJoints = null; + + list.Remove(this); } } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 0d5af0dc8..47f02ca4d 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -2142,6 +2142,8 @@ namespace Barotrauma if (aiTarget != null) aiTarget.Remove(); + if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null; + if (AnimController!=null) AnimController.Remove(); } diff --git a/Subsurface/Source/Characters/Limb.cs b/Subsurface/Source/Characters/Limb.cs index 348c84739..95445a17f 100644 --- a/Subsurface/Source/Characters/Limb.cs +++ b/Subsurface/Source/Characters/Limb.cs @@ -557,12 +557,29 @@ namespace Barotrauma public void Remove() { sprite.Remove(); - if (LightSource != null) LightSource.Remove(); - if (damagedSprite != null) damagedSprite.Remove(); + sprite = null; - body.Remove(); + if (LightSource != null) + { + LightSource.Remove(); + } + if (damagedSprite != null) + { + damagedSprite.Remove(); + damagedSprite = null; + } - if (hitSound != null) hitSound.Remove(); + if (body != null) + { + body.Remove(); + body = null; + } + + if (hitSound != null) + { + hitSound.Remove(); + hitSound = null; + } } } } diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index d77fd77de..75c241465 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -133,7 +133,14 @@ namespace Barotrauma List list = new List(dictionary.Values); foreach (Entity e in list) { - e.Remove(); + try + { + e.Remove(); + } + catch (Exception exception) + { + DebugConsole.ThrowError("Error while removing entity \"" + e.ToString() + "\"", exception); + } } dictionary.Clear(); } diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 1d7a76708..055f82e46 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -334,7 +334,7 @@ namespace Barotrauma base.Remove(); hullList.Remove(this); - if (Submarine == null || !Submarine.Loading) + if (Submarine == null || (!Submarine.Loading && !Submarine.Unloading)) { Item.UpdateHulls(); Gap.UpdateHulls(); From 3c57b9d94594ec1ab0b22bc989438ae819458f3f Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 14 Nov 2016 18:16:30 +0200 Subject: [PATCH 22/57] Fixed particle velocity being set to { NaN, NaN } if drag is applied when velocity is almost zero --- Subsurface/Source/Particles/Particle.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Subsurface/Source/Particles/Particle.cs b/Subsurface/Source/Particles/Particle.cs index d2c65e0f8..9ac4d7b0c 100644 --- a/Subsurface/Source/Particles/Particle.cs +++ b/Subsurface/Source/Particles/Particle.cs @@ -226,9 +226,9 @@ namespace Barotrauma.Particles private void ApplyDrag(float dragCoefficient, float deltaTime) { - if (velocity == Vector2.Zero) return; - + if (Math.Abs(velocity.X) < 0.0001f && Math.Abs(velocity.Y) < 0.0001f) return; float speed = velocity.Length(); + velocity -= (velocity / speed) * Math.Min(speed * speed * prefab.WaterDrag * deltaTime, 1.0f); } From dd5eb69875a5db7e64403b089b84862b1e3966f6 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 14 Nov 2016 20:09:37 +0200 Subject: [PATCH 23/57] Fixed respawned characters getting a different team ID than the rest of the characters (causing them to be displayed as a separate team in the crew menu) --- Subsurface/Source/Networking/RespawnManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Subsurface/Source/Networking/RespawnManager.cs b/Subsurface/Source/Networking/RespawnManager.cs index 987cd1068..dfc8872f8 100644 --- a/Subsurface/Source/Networking/RespawnManager.cs +++ b/Subsurface/Source/Networking/RespawnManager.cs @@ -416,6 +416,7 @@ namespace Barotrauma.Networking bool myCharacter = i >= clients.Count; var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, !myCharacter, false); + character.TeamID = 1; if (myCharacter) { From f7a9a7772195621c88f602e3af92ab6bb998f0da Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 15 Nov 2016 19:53:25 +0200 Subject: [PATCH 24/57] Some debug assertions to help figure out the cause for the body.FixtureList==null & GetHullsInRange crash reports --- Subsurface/Source/Items/Item.cs | 2 ++ Subsurface/Source/Map/Lights/ConvexHull.cs | 15 ++++++++++++-- Subsurface/Source/Map/Lights/LightSource.cs | 22 ++++++++------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 1cbe1e4ee..d1ca4175b 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -770,6 +770,8 @@ namespace Barotrauma if (body == null || !body.Enabled) return; + System.Diagnostics.Debug.Assert(body.FarseerBody.FixtureList != null); + if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) { Submarine prevSub = Submarine; diff --git a/Subsurface/Source/Map/Lights/ConvexHull.cs b/Subsurface/Source/Map/Lights/ConvexHull.cs index 7ab0bc15d..c48285988 100644 --- a/Subsurface/Source/Map/Lights/ConvexHull.cs +++ b/Subsurface/Source/Map/Lights/ConvexHull.cs @@ -44,13 +44,24 @@ namespace Barotrauma.Lights class ConvexHullList { + private List list; + public readonly Submarine Submarine; - public List List; + public List List + { + get { return list; } + set + { + Debug.Assert(value != null); + Debug.Assert(!list.Contains(null)); + list = value; + } + } public ConvexHullList(Submarine submarine) { Submarine = submarine; - List = new List(); + list = new List(); } } diff --git a/Subsurface/Source/Map/Lights/LightSource.cs b/Subsurface/Source/Map/Lights/LightSource.cs index 931c9455f..36f7dbd3b 100644 --- a/Subsurface/Source/Map/Lights/LightSource.cs +++ b/Subsurface/Source/Map/Lights/LightSource.cs @@ -164,15 +164,15 @@ namespace Barotrauma.Lights private List GetHullsInRange(Submarine sub) { + //find the current list of hulls in range var chList = hullsInRange.Find(x => x.Submarine == sub); + //not found -> create one if (chList == null) { chList = new ConvexHullList(sub); hullsInRange.Add(chList); } - List list = chList.List; - Vector2 lightPos = position; if (ParentSub == null) @@ -183,15 +183,12 @@ namespace Barotrauma.Lights if (NeedsHullUpdate) { var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - - list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - chList.List = list; + chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); } } //light is outside, convexhull inside a sub else { - //todo: check lightPos -= sub.Position; Rectangle subBorders = sub.Borders; @@ -201,7 +198,7 @@ namespace Barotrauma.Lights if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) return null; var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); + chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); } } else @@ -215,15 +212,13 @@ namespace Barotrauma.Lights if (NeedsHullUpdate) { var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - - list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - chList.List = list; + chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); } } //light and convexhull are inside different subs else { - if (sub.DockedTo.Contains(ParentSub) && !NeedsHullUpdate) return list; + if (sub.DockedTo.Contains(ParentSub) && !NeedsHullUpdate) return chList.List; lightPos -= (sub.Position - ParentSub.Position); @@ -234,12 +229,11 @@ namespace Barotrauma.Lights if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) return null; var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - chList.List = list; + chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); } } - return list; + return chList.List; } public static List GetHullsInRange(Vector2 position, float range, Submarine ParentSub) From 642a1bdd5461e81ff9ab4452a09549046b42bb98 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 15 Nov 2016 19:56:00 +0200 Subject: [PATCH 25/57] Disabled the "infinite walls" at the edges of the level (don't work correctly with multiple subs), the barrier at the top of the level can't be passed through when outside the borders of the level --- Subsurface/Source/Map/Levels/Level.cs | 22 +++++++++++-------- Subsurface/Source/Map/Levels/LevelRenderer.cs | 7 +++--- Subsurface/Source/Map/SubmarineBody.cs | 15 +++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/Subsurface/Source/Map/Levels/Level.cs b/Subsurface/Source/Map/Levels/Level.cs index 0f2ff1a6c..e81bf75e3 100644 --- a/Subsurface/Source/Map/Levels/Level.cs +++ b/Subsurface/Source/Map/Levels/Level.cs @@ -54,7 +54,7 @@ namespace Barotrauma public const int GridCellSize = 2000; private List[,] cellGrid; - private WrappingWall[,] wrappingWalls; + //private WrappingWall[,] wrappingWalls; //private float shaftHeight; @@ -97,10 +97,10 @@ namespace Barotrauma get { return ruins; } } - public WrappingWall[,] WrappingWalls - { - get { return wrappingWalls; } - } + //public WrappingWall[,] WrappingWalls + //{ + // get { return wrappingWalls; } + //} public string Seed { @@ -440,6 +440,7 @@ namespace Barotrauma renderer.PlaceSprites(generationParams.BackgroundSpriteAmount); + /* wrappingWalls = new WrappingWall[2, 2]; Rectangle ignoredArea = new Rectangle((int)startPosition.X, 0, (int)(endPosition.X - startPosition.X), borders.Height); @@ -465,7 +466,7 @@ namespace Barotrauma { cells.AddRange(wrappingWalls[side, i].Cells); } - } + }*/ ShaftBody = BodyFactory.CreateEdge(GameMain.World, ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)), @@ -809,10 +810,11 @@ namespace Barotrauma public void Update(float deltaTime) { + /* if (Submarine.MainSub != null) { WrappingWall.UpdateWallShift(Submarine.MainSub.WorldPosition, wrappingWalls); - } + }*/ if (Hull.renderer != null) { @@ -884,6 +886,7 @@ namespace Barotrauma } } + /* if (wrappingWalls == null) return cells; for (int side = 0; side < 2; side++) @@ -899,7 +902,7 @@ namespace Barotrauma cells.Add(cell); } } - } + }*/ return cells; } @@ -918,6 +921,7 @@ namespace Barotrauma ruins = null; } + /* if (wrappingWalls!=null) { for (int side = 0; side < 2; side++) @@ -929,7 +933,7 @@ namespace Barotrauma } wrappingWalls = null; - } + }*/ cells = null; diff --git a/Subsurface/Source/Map/Levels/LevelRenderer.cs b/Subsurface/Source/Map/Levels/LevelRenderer.cs index f1e5148c1..0910925c2 100644 --- a/Subsurface/Source/Map/Levels/LevelRenderer.cs +++ b/Subsurface/Source/Map/Levels/LevelRenderer.cs @@ -219,6 +219,7 @@ namespace Barotrauma graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(bodyVertices.VertexCount / 3.0f)); + /* for (int side = 0; side < 2; side++) { for (int i = 0; i < 2; i++) @@ -234,7 +235,7 @@ namespace Barotrauma PrimitiveType.TriangleList, 0, (int)Math.Floor(level.WrappingWalls[side, i].BodyVertices.VertexCount / 3.0f)); } - } + }*/ graphicsDevice.SetVertexBuffer(wallVertices); @@ -243,7 +244,7 @@ namespace Barotrauma basicEffect.CurrentTechnique = basicEffect.Techniques["BasicEffect_Texture"]; basicEffect.CurrentTechnique.Passes[0].Apply(); graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wallVertices.VertexCount / 3.0f)); - + /* for (int side = 0; side < 2; side++) { for (int i = 0; i < 2; i++) @@ -260,7 +261,7 @@ namespace Barotrauma (int)Math.Floor(level.WrappingWalls[side, i].WallVertices.VertexCount / 3.0f)); } - } + }*/ } diff --git a/Subsurface/Source/Map/SubmarineBody.cs b/Subsurface/Source/Map/SubmarineBody.cs index c17d6c302..c9c904ef9 100644 --- a/Subsurface/Source/Map/SubmarineBody.cs +++ b/Subsurface/Source/Map/SubmarineBody.cs @@ -243,6 +243,21 @@ namespace Barotrauma return; } + //if outside left or right edge of the level + if (Position.X < 0 || Position.X > Level.Loaded.Size.X) + { + Rectangle worldBorders = Borders; + worldBorders.Location += Position.ToPoint(); + + //push the sub back below the upper "barrier" of the level + if (worldBorders.Y > Level.Loaded.Size.Y) + { + Body.LinearVelocity = new Vector2( + Body.LinearVelocity.X, + Math.Min(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.Size.Y - worldBorders.Y))); + } + } + //------------------------- Vector2 totalForce = CalculateBuoyancy(); From 184c6858cdbcd94f0118f7e840dfd23b7ecbe565 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 15 Nov 2016 20:48:52 +0200 Subject: [PATCH 26/57] Removing orphans in PathFinder.GenerateNodes, fixed autopilot steering the wrong way if clicking an already selected destination tickbox --- Subsurface/Source/Characters/AI/PathFinder.cs | 8 ++------ Subsurface/Source/Items/Components/Machines/Steering.cs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Subsurface/Source/Characters/AI/PathFinder.cs b/Subsurface/Source/Characters/AI/PathFinder.cs index 7ddef2f11..78846b22c 100644 --- a/Subsurface/Source/Characters/AI/PathFinder.cs +++ b/Subsurface/Source/Characters/AI/PathFinder.cs @@ -61,6 +61,7 @@ namespace Barotrauma } var nodeList = nodes.Values.ToList(); + nodeList.RemoveAll(n => n.connections.Count == 0); foreach (PathNode node in nodeList) { node.distances = new List(); @@ -69,6 +70,7 @@ namespace Barotrauma node.distances.Add(Vector2.Distance(node.position, node.connections[i].position)); } } + return nodeList; } } @@ -198,12 +200,6 @@ namespace Barotrauma { Vector2 nodePos = node.Position; - //if node waypoint is one of submarine waypoints outside the sub, transform position - //if (node.Waypoint!=null && node.Waypoint.Submarine != null && node.Waypoint.CurrentHull==null) - //{ - // nodePos -= node.Waypoint.Submarine.Position; - //} - float dist = Vector2.Distance(end, nodePos); if (dist < closestDist || endNode == null) { diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index c10a905fa..c9250a5cc 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -424,10 +424,10 @@ namespace Barotrauma.Items.Components maintainPosTickBox.Selected = false; posToMaintain = null; + tickBox.Selected = true; UpdatePath(); - tickBox.Selected = true; return true; } From d2c17274fe1ce0848ca0ad5b60e91ab2942a428d Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Tue, 15 Nov 2016 22:26:36 -0300 Subject: [PATCH 27/57] GUI elements now respect render order + some minor distance comparison optimization --- Launcher2/LauncherMain.cs | 2 +- .../Source/Characters/Animation/Ragdoll.cs | 4 +- Subsurface/Source/Characters/Character.cs | 30 ++++++++--- Subsurface/Source/Characters/CharacterHUD.cs | 27 +++++++++- Subsurface/Source/DebugConsole.cs | 10 +++- Subsurface/Source/GUI/GUI.cs | 23 ++++++++- Subsurface/Source/GUI/GUIComponent.cs | 51 +++++++++++++++++-- Subsurface/Source/GUI/GUIDropDown.cs | 7 +++ Subsurface/Source/GUI/GUIFrame.cs | 2 +- Subsurface/Source/GUI/GUIListBox.cs | 14 +++++ Subsurface/Source/GUI/GUITickBox.cs | 16 ++++-- Subsurface/Source/GameMain.cs | 26 +++++++++- Subsurface/Source/GameSession/CrewManager.cs | 7 +++ .../Source/GameSession/GameModes/GameMode.cs | 2 + .../GameModes/Tutorials/TutorialMode.cs | 5 ++ .../GameModes/Tutorials/TutorialType.cs | 5 ++ Subsurface/Source/GameSession/GameSession.cs | 7 +++ .../Source/Items/Components/ItemComponent.cs | 2 + .../Components/Machines/Deconstructor.cs | 5 ++ .../Items/Components/Machines/Engine.cs | 6 ++- .../Items/Components/Machines/Fabricator.cs | 5 ++ .../Source/Items/Components/Machines/Pump.cs | 5 ++ .../Source/Items/Components/Machines/Radar.cs | 6 +++ .../Items/Components/Machines/Reactor.cs | 5 ++ .../Items/Components/Machines/Steering.cs | 24 ++++++--- .../Items/Components/Power/PowerContainer.cs | 5 ++ .../Items/Components/Power/PowerTransfer.cs | 5 ++ Subsurface/Source/Items/FixRequirement.cs | 7 +++ Subsurface/Source/Items/Item.cs | 23 ++++++++- Subsurface/Source/Map/MapEntity.cs | 21 +++++++- Subsurface/Source/Networking/GameServer.cs | 8 +++ Subsurface/Source/Networking/NetworkMember.cs | 10 ++++ Subsurface/Source/Screens/EditMapScreen.cs | 50 +++++++++++++++--- Subsurface/Source/Screens/GameScreen.cs | 18 ++++++- Subsurface/Source/Screens/LobbyScreen.cs | 8 +++ Subsurface/Source/Screens/MainMenuScreen.cs | 6 +++ Subsurface/Source/Screens/NetLobbyScreen.cs | 22 ++++++-- Subsurface/Source/Screens/Screen.cs | 4 ++ Subsurface/Source/Screens/ServerListScreen.cs | 5 ++ 39 files changed, 441 insertions(+), 47 deletions(-) diff --git a/Launcher2/LauncherMain.cs b/Launcher2/LauncherMain.cs index 25525ebb5..b8d166735 100644 --- a/Launcher2/LauncherMain.cs +++ b/Launcher2/LauncherMain.cs @@ -196,7 +196,7 @@ namespace Launcher2 var messageBox = GUIMessageBox.MessageBoxes.Peek(); if (messageBox != null) { - GUIComponent.MouseOn = messageBox; + GUIComponent.ForceMouseOn(messageBox); messageBox.Update(deltaTime); return; } diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 050bf0efc..aee5ff37d 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -1019,7 +1019,7 @@ namespace Barotrauma if (lerp) { limb.body.TargetPosition = movePos; - limb.body.MoveToTargetPosition(Vector2.Distance(limb.SimPosition, movePos) < 10.0f); + limb.body.MoveToTargetPosition(Vector2.DistanceSquared(limb.SimPosition, movePos) < 100.0f); } else { @@ -1041,7 +1041,7 @@ namespace Barotrauma //if the ragdoll is too far from the collider, disable collisions until it's close enough //(in case the ragdoll has gotten stuck somewhere) - if (Vector2.Distance(collider.SimPosition, MainLimb.SimPosition) > allowedDist) + if (Vector2.DistanceSquared(collider.SimPosition, MainLimb.SimPosition) > allowedDist*allowedDist) { if (!collisionsDisabled) { diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 47f02ca4d..895f9933d 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -857,7 +857,7 @@ namespace Barotrauma if (selectedCharacter!=null) { - if (Vector2.Distance(selectedCharacter.WorldPosition, WorldPosition) > 300.0f || !selectedCharacter.CanBeSelected) + if (Vector2.DistanceSquared(selectedCharacter.WorldPosition, WorldPosition) > 90000.0f || !selectedCharacter.CanBeSelected) { DeselectCharacter(controlled == this); } @@ -952,7 +952,7 @@ namespace Barotrauma maxDist = 150.0f; } - if (Vector2.Distance(WorldPosition, item.WorldPosition) < maxDist || + if (Vector2.DistanceSquared(WorldPosition, item.WorldPosition) < maxDist*maxDist || item.IsInsideTrigger(WorldPosition)) { return true; @@ -994,10 +994,10 @@ namespace Barotrauma { if (c == this || !c.enabled) continue; - if (Vector2.Distance(SimPosition, c.SimPosition) > maxDist) continue; + if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist*maxDist) continue; - float dist = Vector2.Distance(mouseSimPos, c.SimPosition); - if (dist < maxDist && (closestCharacter==null || dist 1.0f) + if (Lights.LightManager.ViewTarget == this && Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f) { Body body = Submarine.PickBody(AnimController.Limbs[0].SimPosition, mouseSimPos); Structure structure = null; @@ -1106,7 +1106,7 @@ namespace Barotrauma if (closestCharacter != null && closestItem != null) { - if (Vector2.Distance(closestCharacter.SimPosition, mouseSimPos) < ConvertUnits.ToSimUnits(closestItemDist)) + if (Vector2.DistanceSquared(closestCharacter.SimPosition, mouseSimPos) < ConvertUnits.ToSimUnits(closestItemDist)*ConvertUnits.ToSimUnits(closestItemDist)) { if (selectedConstruction != closestItem) closestItem = null; } @@ -1172,6 +1172,14 @@ namespace Barotrauma } } + public static void AddAllToGUIUpdateList() + { + for (int i = 0; i < CharacterList.Count; i++) + { + CharacterList[i].AddToGUIUpdateList(); + } + } + public static void UpdateAll(Camera cam, float deltaTime) { //if (NewCharacterQueue.Count>0) @@ -1185,6 +1193,14 @@ namespace Barotrauma } } + public virtual void AddToGUIUpdateList() + { + if (controlled == this) + { + CharacterHUD.AddToGUIUpdateList(this); + } + } + public virtual void Update(Camera cam, float deltaTime) { if (!Enabled) return; diff --git a/Subsurface/Source/Characters/CharacterHUD.cs b/Subsurface/Source/Characters/CharacterHUD.cs index 0ec2b0e09..f8f81ea8c 100644 --- a/Subsurface/Source/Characters/CharacterHUD.cs +++ b/Subsurface/Source/Characters/CharacterHUD.cs @@ -32,6 +32,31 @@ namespace Barotrauma damageOverlayTimer = MathHelper.Clamp(amount * 0.1f, 0.2f, 5.0f); } + public static void AddToGUIUpdateList(Character character) + { + if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList(); + + if (suicideButton != null && suicideButton.Visible) suicideButton.AddToGUIUpdateList(); + + if (!character.IsUnconscious && character.Stun <= 0.0f) + { + + if (character.Inventory != null) + { + for (int i = 0; i < character.Inventory.Items.Length - 1; i++) + { + var item = character.Inventory.Items[i]; + if (item == null || CharacterInventory.limbSlots[i] == InvSlotType.Any) continue; + + foreach (ItemComponent ic in item.components) + { + if (ic.DrawHudWhenEquipped) ic.AddToGUIUpdateList(); + } + } + } + } + } + public static void Update(float deltaTime, Character character) { if (drowningBar != null) @@ -228,7 +253,7 @@ namespace Barotrauma suicideButton.OnClicked = (button, userData) => { - GUIComponent.MouseOn = null; + GUIComponent.ForceMouseOn(null); if (Character.Controlled != null) { Character.Controlled.Kill(Character.Controlled.CauseOfDeath); diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 16083a694..63f4263ce 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -78,6 +78,14 @@ namespace Barotrauma } + public static void AddToGUIUpdateList() + { + if (isOpen) + { + frame.AddToGUIUpdateList(); + } + } + public static void Update(GameMain game, float deltaTime) { if (PlayerInput.KeyHit(Keys.F3)) @@ -89,7 +97,7 @@ namespace Barotrauma } else { - GUIComponent.MouseOn = null; + GUIComponent.ForceMouseOn(null); textBox.Deselect(); } diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index 2591365e3..18b8f3c59 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -499,6 +499,28 @@ namespace Barotrauma cursor.Draw(spriteBatch, PlayerInput.MousePosition); } + public static void AddToGUIUpdateList() + { + if (pauseMenuOpen) + { + pauseMenu.AddToGUIUpdateList(); + } + + if (settingsMenuOpen) + { + GameMain.Config.SettingsFrame.AddToGUIUpdateList(); + } + + if (GUIMessageBox.MessageBoxes.Count > 0) + { + var messageBox = GUIMessageBox.MessageBoxes.Peek(); + if (messageBox != null) + { + messageBox.AddToGUIUpdateList(); + } + } + } + public static void Update(float deltaTime) { if (pauseMenuOpen) @@ -516,7 +538,6 @@ namespace Barotrauma var messageBox = GUIMessageBox.MessageBoxes.Peek(); if (messageBox != null) { - GUIComponent.MouseOn = messageBox; messageBox.Update(deltaTime); } } diff --git a/Subsurface/Source/GUI/GUIComponent.cs b/Subsurface/Source/GUI/GUIComponent.cs index f3cabe95b..dc8e9d7cc 100644 --- a/Subsurface/Source/GUI/GUIComponent.cs +++ b/Subsurface/Source/GUI/GUIComponent.cs @@ -10,9 +10,47 @@ namespace Barotrauma public abstract class GUIComponent { const float FlashDuration = 1.5f; - - public static GUIComponent MouseOn; + public static GUIComponent MouseOn + { + get; + private set; + } + public static void ForceMouseOn(GUIComponent c) + { + MouseOn = c; + } + + protected static List ComponentsToUpdate = new List(); + + public virtual void AddToGUIUpdateList() + { + if (!Visible) return; + if (ComponentsToUpdate.Contains(this)) return; + ComponentsToUpdate.Add(this); + children.ForEach(c => c.AddToGUIUpdateList()); + } + + public static void ClearUpdateList() + { + ComponentsToUpdate.Clear(); + } + + public static GUIComponent UpdateMouseOn() + { + MouseOn = null; + for (int i=ComponentsToUpdate.Count-1;i>=0;i--) + { + GUIComponent c = ComponentsToUpdate[i]; + if (c.MouseRect.Contains(PlayerInput.MousePosition)) + { + MouseOn = c; + break; + } + } + return MouseOn; + } + protected static KeyboardDispatcher keyboardDispatcher; public enum ComponentState { None, Hover, Selected}; @@ -97,6 +135,11 @@ namespace Barotrauma } } } + + public virtual Rectangle MouseRect + { + get { return CanBeFocused ? rect : Rectangle.Empty; } + } public List sprites; //public Alignment SpriteAlignment { get; set; } @@ -304,7 +347,7 @@ namespace Barotrauma if (flashTimer>0.0f) flashTimer -= deltaTime; - if (CanBeFocused) + /*if (CanBeFocused) { if (rect.Contains(PlayerInput.MousePosition)) { @@ -315,7 +358,7 @@ namespace Barotrauma if (MouseOn == this) MouseOn = null; } - } + }*/ //use a fixed list since children can change their order in the main children list //TODO: maybe find a more efficient way of handling changes in list order diff --git a/Subsurface/Source/GUI/GUIDropDown.cs b/Subsurface/Source/GUI/GUIDropDown.cs index b4ae51dc7..55b357a97 100644 --- a/Subsurface/Source/GUI/GUIDropDown.cs +++ b/Subsurface/Source/GUI/GUIDropDown.cs @@ -166,6 +166,13 @@ namespace Barotrauma return true; } + public override void AddToGUIUpdateList() + { + base.AddToGUIUpdateList(); + button.AddToGUIUpdateList(); + if (Dropped) listBox.AddToGUIUpdateList(); + } + public override void Update(float deltaTime) { if (!Visible) return; diff --git a/Subsurface/Source/GUI/GUIFrame.cs b/Subsurface/Source/GUI/GUIFrame.cs index c3adb43db..bf15905f6 100644 --- a/Subsurface/Source/GUI/GUIFrame.cs +++ b/Subsurface/Source/GUI/GUIFrame.cs @@ -36,7 +36,7 @@ namespace Barotrauma //if (style != null) ApplyStyle(style); } - + public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { if (!Visible) return; diff --git a/Subsurface/Source/GUI/GUIListBox.cs b/Subsurface/Source/GUI/GUIListBox.cs index d9630dbf1..117d193f2 100644 --- a/Subsurface/Source/GUI/GUIListBox.cs +++ b/Subsurface/Source/GUI/GUIListBox.cs @@ -264,6 +264,20 @@ namespace Barotrauma } } + public override void AddToGUIUpdateList() + { + base.AddToGUIUpdateList(); + if (scrollBarEnabled && !scrollBarHidden) scrollBar.AddToGUIUpdateList(); + } + + public override Rectangle MouseRect + { + get + { + return Rectangle.Empty; + } + } + public override void Update(float deltaTime) { if (!Visible) return; diff --git a/Subsurface/Source/GUI/GUITickBox.cs b/Subsurface/Source/GUI/GUITickBox.cs index 4421ef9c9..8ed501baa 100644 --- a/Subsurface/Source/GUI/GUITickBox.cs +++ b/Subsurface/Source/GUI/GUITickBox.cs @@ -55,6 +55,11 @@ namespace Barotrauma } } + public override Rectangle MouseRect + { + get { return box.Rect; } + } + public GUITickBox(Rectangle rect, string label, Alignment alignment, GUIComponent parent) : this(rect, label, alignment, GUI.Font, parent) { @@ -69,6 +74,7 @@ namespace Barotrauma box = new GUIFrame(rect, Color.DarkGray, null, this); box.HoverColor = Color.Gray; box.SelectedColor = Color.DarkGray; + box.CanBeFocused = false; text = new GUITextBlock(new Rectangle(rect.Right + 10, rect.Y+2, 20, rect.Height), label, GUI.Style, this, font); @@ -76,19 +82,19 @@ namespace Barotrauma Enabled = true; } - + public override void Update(float deltaTime) { if (!Visible || !Enabled) return; - if (MouseOn != null && MouseOn != this && !MouseOn.IsParentOf(this)) return; + //if (MouseOn != null && MouseOn != this && !MouseOn.IsParentOf(this)) return; - if (text.Rect.Contains(PlayerInput.MousePosition)) MouseOn = this; + //if (text.Rect.Contains(PlayerInput.MousePosition)) MouseOn = this; - if (box.Rect.Contains(PlayerInput.MousePosition)) + if (MouseOn==this)//box.Rect.Contains(PlayerInput.MousePosition)) { //ToolTip = this.ToolTip; - MouseOn = this; + //MouseOn = this; box.State = ComponentState.Hover; diff --git a/Subsurface/Source/GameMain.cs b/Subsurface/Source/GameMain.cs index 14f2ff8b9..db7cca7db 100644 --- a/Subsurface/Source/GameMain.cs +++ b/Subsurface/Source/GameMain.cs @@ -327,10 +327,26 @@ namespace Barotrauma if (PlayerInput.KeyHit(Keys.Escape)) GUI.TogglePauseMenu(); - DebugConsole.Update(this, (float)Timing.Step); + GUIComponent.ClearUpdateList(); + DebugConsole.AddToGUIUpdateList(); paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen) && (NetworkMember == null || !NetworkMember.GameStarted); + + if (!paused) + { + Screen.Selected.AddToGUIUpdateList(); + } + + if (NetworkMember != null) + { + NetworkMember.AddToGUIUpdateList(); + } + + GUI.AddToGUIUpdateList(); + GUIComponent.UpdateMouseOn(); + + DebugConsole.Update(this, (float)Timing.Step); if (!paused) { @@ -371,6 +387,14 @@ namespace Barotrauma { Screen.Selected.Draw(deltaTime, GraphicsDevice, spriteBatch); } + + if (!DebugDraw) return; + if (GUIComponent.MouseOn!=null) + { + spriteBatch.Begin(); + GUI.DrawRectangle(spriteBatch, GUIComponent.MouseOn.MouseRect, Color.Lime); + spriteBatch.End(); + } } static bool waitForKeyHit = true; diff --git a/Subsurface/Source/GameSession/CrewManager.cs b/Subsurface/Source/GameSession/CrewManager.cs index 8315d18e3..4d2024d0c 100644 --- a/Subsurface/Source/GameSession/CrewManager.cs +++ b/Subsurface/Source/GameSession/CrewManager.cs @@ -157,6 +157,13 @@ namespace Barotrauma //new GUIImage(new Rectangle(-10, -5, 0, 0), character.AnimController.Limbs[0].sprite, Alignment.Left, frame); } + public void AddToGUIUpdateList() + { + guiFrame.AddToGUIUpdateList(); + if (commander.Frame != null) commander.Frame.AddToGUIUpdateList(); + if (crewFrameOpen) crewFrame.AddToGUIUpdateList(); + } + public void Update(float deltaTime) { guiFrame.Update(deltaTime); diff --git a/Subsurface/Source/GameSession/GameModes/GameMode.cs b/Subsurface/Source/GameSession/GameModes/GameMode.cs index 34ca97f4d..ae25c4ef2 100644 --- a/Subsurface/Source/GameSession/GameModes/GameMode.cs +++ b/Subsurface/Source/GameSession/GameModes/GameMode.cs @@ -77,6 +77,8 @@ namespace Barotrauma public virtual void MsgBox() { } + public virtual void AddToGUIUpdateList() { } + public virtual void Update(float deltaTime) { //if (!isRunning) return; diff --git a/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialMode.cs b/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialMode.cs index 7e59a33a6..9828e2dd4 100644 --- a/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialMode.cs +++ b/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialMode.cs @@ -27,6 +27,11 @@ namespace Barotrauma } + public override void AddToGUIUpdateList() + { + tutorialType.AddToGUIUpdateList(); + } + public override void Update(float deltaTime) { base.Update(deltaTime); diff --git a/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialType.cs b/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialType.cs index d84ef4968..a46777f2a 100644 --- a/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialType.cs +++ b/Subsurface/Source/GameSession/GameModes/Tutorials/TutorialType.cs @@ -75,6 +75,11 @@ namespace Barotrauma.Tutorials CoroutineManager.StartCoroutine(UpdateState()); } + public virtual void AddToGUIUpdateList() + { + if (infoBox != null) infoBox.AddToGUIUpdateList(); + } + public virtual void Update(float deltaTime) { if (character!=null) diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 45d95adfc..681b47fd5 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -360,6 +360,13 @@ namespace Barotrauma } + public void AddToGUIUpdateList() + { + if (gameMode != null) gameMode.AddToGUIUpdateList(); + + if (infoFrame != null) infoButton.AddToGUIUpdateList(); + } + public void Update(float deltaTime) { TaskManager.Update(deltaTime); diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index 1a811bf91..82ade6dd9 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -469,6 +469,8 @@ namespace Barotrauma.Items.Components public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { } + public virtual void AddToGUIUpdateList() { } + public virtual void UpdateHUD(Character character) { } /// true if the operation was completed diff --git a/Subsurface/Source/Items/Components/Machines/Deconstructor.cs b/Subsurface/Source/Items/Components/Machines/Deconstructor.cs index c0cb4b99f..5f2b53114 100644 --- a/Subsurface/Source/Items/Components/Machines/Deconstructor.cs +++ b/Subsurface/Source/Items/Components/Machines/Deconstructor.cs @@ -85,6 +85,11 @@ namespace Barotrauma.Items.Components GuiFrame.Draw(spriteBatch); } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update((float)Timing.Step); diff --git a/Subsurface/Source/Items/Components/Machines/Engine.cs b/Subsurface/Source/Items/Components/Machines/Engine.cs index 297d72165..3ecec7d7c 100644 --- a/Subsurface/Source/Items/Components/Machines/Engine.cs +++ b/Subsurface/Source/Items/Components/Machines/Engine.cs @@ -116,7 +116,11 @@ namespace Barotrauma.Items.Components //GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true); spriteBatch.DrawString(GUI.Font, "Force: " + (int)(targetForce) + " %", new Vector2(GuiFrame.Rect.X + 30, GuiFrame.Rect.Y + 30), Color.White); - + } + + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); } public override void UpdateHUD(Character character) diff --git a/Subsurface/Source/Items/Components/Machines/Fabricator.cs b/Subsurface/Source/Items/Components/Machines/Fabricator.cs index aecb0e0a6..f594f22f6 100644 --- a/Subsurface/Source/Items/Components/Machines/Fabricator.cs +++ b/Subsurface/Source/Items/Components/Machines/Fabricator.cs @@ -385,6 +385,11 @@ namespace Barotrauma.Items.Components GuiFrame.Draw(spriteBatch); } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { FabricableItem targetItem = itemList.SelectedData as FabricableItem; diff --git a/Subsurface/Source/Items/Components/Machines/Pump.cs b/Subsurface/Source/Items/Components/Machines/Pump.cs index f45e3ab25..c388ff63a 100644 --- a/Subsurface/Source/Items/Components/Machines/Pump.cs +++ b/Subsurface/Source/Items/Components/Machines/Pump.cs @@ -162,6 +162,11 @@ namespace Barotrauma.Items.Components } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update(1.0f / 60.0f); diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index 49a3b12c5..06b99230b 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -62,6 +62,7 @@ namespace Barotrauma.Items.Components return true; }; + GuiFrame.CanBeFocused = false; } public override void Update(float deltaTime, Camera cam) @@ -100,6 +101,11 @@ namespace Barotrauma.Items.Components return pingState > 1.0f; } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update((float)Timing.Step); diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index 9012c5c4f..02849c33a 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -479,6 +479,11 @@ namespace Barotrauma.Items.Components //y = y - 260; } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update(1.0f / 60.0f); diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index c10a905fa..bcf4cb856 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -214,20 +214,28 @@ namespace Barotrauma.Items.Components if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(velRect.Center.X, velRect.Center.Y)) < 200.0f) { GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X -10, (int)targetVelPos.Y - 10, 20, 20), Color.Red); - - if (PlayerInput.LeftButtonHeld()) - { - TargetVelocity = PlayerInput.MousePosition - new Vector2(velRect.Center.X, velRect.Center.Y); - targetVelocity.Y = -targetVelocity.Y; - - valueChanged = true; - } } } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update(1.0f / 60.0f); + + if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y)) < 200.0f) + { + if (PlayerInput.LeftButtonHeld()) + { + TargetVelocity = PlayerInput.MousePosition - new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y); + targetVelocity.Y = -targetVelocity.Y; + + valueChanged = true; + } + } } private void UpdateAutoPilot(float deltaTime) diff --git a/Subsurface/Source/Items/Components/Power/PowerContainer.cs b/Subsurface/Source/Items/Components/Power/PowerContainer.cs index cf34a92f6..f793dce9d 100644 --- a/Subsurface/Source/Items/Components/Power/PowerContainer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerContainer.cs @@ -246,6 +246,11 @@ namespace Barotrauma.Items.Components spriteBatch.DrawString(GUI.Font, "Recharge rate: " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", new Vector2(x + 30, y + 95), Color.White); } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update(1.0f / 60.0f); diff --git a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs index dbdc7e932..e8bae85b8 100644 --- a/Subsurface/Source/Items/Components/Power/PowerTransfer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerTransfer.cs @@ -181,6 +181,11 @@ namespace Barotrauma.Items.Components spriteBatch.DrawString(GUI.Font, "Load: " + (int)powerLoad + " kW", new Vector2(x + 30, y + 100), Color.White); } + public override void AddToGUIUpdateList() + { + GuiFrame.AddToGUIUpdateList(); + } + public override void UpdateHUD(Character character) { GuiFrame.Update(1.0f / 60.0f); diff --git a/Subsurface/Source/Items/FixRequirement.cs b/Subsurface/Source/Items/FixRequirement.cs index 804da105d..c281b6314 100644 --- a/Subsurface/Source/Items/FixRequirement.cs +++ b/Subsurface/Source/Items/FixRequirement.cs @@ -192,6 +192,13 @@ namespace Barotrauma frame.Draw(spriteBatch); } + public static void AddToGUIUpdateList() + { + if (frame == null) return; + + frame.AddToGUIUpdateList(); + } + public static void UpdateHud(Item item, Character character) { if (frame == null || frame.UserData != item) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 1cbe1e4ee..d8f2aaf91 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1139,6 +1139,27 @@ namespace Barotrauma } } + public override void AddToGUIUpdateList() + { + if (condition <= 0.0f) + { + FixRequirement.AddToGUIUpdateList(); + return; + } + + if (HasInGameEditableProperties) + { + if (editingHUD != null) editingHUD.AddToGUIUpdateList(); + } + + foreach (ItemComponent ic in components) + { + ic.AddToGUIUpdateList(); + } + + if (Screen.Selected is EditMapScreen && editingHUD != null) editingHUD.AddToGUIUpdateList(); + } + public virtual void UpdateHUD(Camera cam, Character character) { if (condition <= 0.0f) @@ -1388,7 +1409,7 @@ namespace Barotrauma ic.ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker); ic.PlaySound(ActionType.OnPicked, picker.WorldPosition); - if (picker==Character.Controlled) GUIComponent.MouseOn = null; + if (picker==Character.Controlled) GUIComponent.ForceMouseOn(null); if (ic.CanBeSelected) selected = true; } diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 288dff844..9ae6dea14 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -18,10 +18,24 @@ namespace Barotrauma //which entities have been selected for editing private static List selectedList = new List(); + public static List SelectedList + { + get + { + return selectedList; + } + } private static List copiedList = new List(); protected static GUIComponent editingHUD; - + public static GUIComponent EditingHUD + { + get + { + return editingHUD; + } + } + protected static Vector2 selectionPos = Vector2.Zero; protected static Vector2 selectionSize = Vector2.Zero; @@ -636,6 +650,11 @@ namespace Barotrauma Move(-relative * 2.0f); } + + public virtual void AddToGUIUpdateList() + { + if (editingHUD != null) editingHUD.AddToGUIUpdateList(); + } public virtual void UpdateEditing(Camera cam) { } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 09c19dfcc..98fc5b4b4 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -288,6 +288,14 @@ namespace Barotrauma.Networking Log("Master server responded", Color.Cyan); } + public override void AddToGUIUpdateList() + { + if (started) base.AddToGUIUpdateList(); + + if (settingsFrame != null) settingsFrame.AddToGUIUpdateList(); + if (log.LogFrame != null) log.LogFrame.AddToGUIUpdateList(); + } + public override void Update(float deltaTime) { if (ShowNetStats) netStats.Update(deltaTime); diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index bb9f1fd10..69b7474d4 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -338,6 +338,16 @@ namespace Barotrauma.Networking public virtual void KickPlayer(string kickedName, bool ban, bool range = false) { } + public virtual void AddToGUIUpdateList() + { + if (gameStarted && Screen.Selected == GameMain.GameScreen) + { + inGameHUD.AddToGUIUpdateList(); + + GameMain.GameSession.CrewManager.AddToGUIUpdateList(); + } + } + public virtual void Update(float deltaTime) { if (gameStarted && Screen.Selected == GameMain.GameScreen) diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 9d5544727..6bae0e7ed 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -250,8 +250,8 @@ namespace Barotrauma public override void Select() { base.Select(); - - GUIComponent.MouseOn = null; + + GUIComponent.ForceMouseOn(null); characterMode = false; if (Submarine.MainSub != null) @@ -278,7 +278,7 @@ namespace Barotrauma { base.Deselect(); - GUIComponent.MouseOn = null; + GUIComponent.ForceMouseOn(null); MapEntityPrefab.Selected = null; @@ -768,7 +768,7 @@ namespace Barotrauma MapEntityPrefab.SelectPrefab(obj); selectedTab = -1; - GUIComponent.MouseOn = null; + GUIComponent.ForceMouseOn(null); return false; } @@ -803,6 +803,43 @@ namespace Barotrauma previouslyUsedList.children.Insert(0, textBlock); } + public override void AddToGUIUpdateList() + { + if (tutorial != null) tutorial.AddToGUIUpdateList(); + + if (MapEntity.SelectedList.Count == 1) + { + MapEntity.SelectedList[0].AddToGUIUpdateList(); + } + + leftPanel.AddToGUIUpdateList(); + topPanel.AddToGUIUpdateList(); + + if (wiringMode) + { + wiringToolPanel.AddToGUIUpdateList(); + } + + if (loadFrame != null) + { + loadFrame.AddToGUIUpdateList(); + } + else if (saveFrame != null) + { + saveFrame.AddToGUIUpdateList(); + } + else if (selectedTab > -1) + { + GUItabs[selectedTab].AddToGUIUpdateList(); + } + + if ((characterMode || wiringMode) && dummyCharacter != null) + { + CharacterHUD.AddToGUIUpdateList(dummyCharacter); + } + + GUI.AddToGUIUpdateList(); + } /// /// Allows the game to run logic such as updating the world, @@ -851,16 +888,15 @@ namespace Barotrauma } else { - MapEntity.UpdateSelecting(cam); } - GUIComponent.MouseOn = null; + //GUIComponent.ForceMouseOn(null); if (!characterMode && !wiringMode) { if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(cam); - + MapEntity.UpdateEditor(cam); } diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index c77d130b3..4d31a95d7 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -88,7 +88,21 @@ namespace Barotrauma Sounds.SoundManager.LowPassHFGain = 1.0f; } - + + public override void AddToGUIUpdateList() + { + if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList(); + + if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null) + { + if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem) + { + Character.Controlled.SelectedConstruction.AddToGUIUpdateList(); + } + } + Character.AddAllToGUIUpdateList(); + } + /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. @@ -132,7 +146,7 @@ namespace Barotrauma } } Character.UpdateAll(cam, (float)deltaTime); - + BackgroundCreatureManager.Update(cam, (float)deltaTime); GameMain.ParticleManager.Update((float)deltaTime); diff --git a/Subsurface/Source/Screens/LobbyScreen.cs b/Subsurface/Source/Screens/LobbyScreen.cs index 6052ae1d8..50fb6747f 100644 --- a/Subsurface/Source/Screens/LobbyScreen.cs +++ b/Subsurface/Source/Screens/LobbyScreen.cs @@ -341,6 +341,14 @@ namespace Barotrauma return false; } + public override void AddToGUIUpdateList() + { + base.AddToGUIUpdateList(); + + topPanel.AddToGUIUpdateList(); + bottomPanel[selectedRightPanel].AddToGUIUpdateList(); + } + public override void Update(double deltaTime) { base.Update(deltaTime); diff --git a/Subsurface/Source/Screens/MainMenuScreen.cs b/Subsurface/Source/Screens/MainMenuScreen.cs index a1d6783bb..e520cdf14 100644 --- a/Subsurface/Source/Screens/MainMenuScreen.cs +++ b/Subsurface/Source/Screens/MainMenuScreen.cs @@ -467,6 +467,12 @@ namespace Barotrauma menuTabs[(int)Tab.LoadGame].RemoveChild(prevFrame); } + public override void AddToGUIUpdateList() + { + buttonsTab.AddToGUIUpdateList(); + if (selectedTab > 0) menuTabs[selectedTab].AddToGUIUpdateList(); + } + public override void Update(double deltaTime) { buttonsTab.Update((float)deltaTime); diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 074a20ec1..7beee3ce4 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -916,6 +916,24 @@ namespace Barotrauma playerList.ClearChildren(); } + public override void AddToGUIUpdateList() + { + base.AddToGUIUpdateList(); + + if (jobInfoFrame != null) + { + jobInfoFrame.AddToGUIUpdateList(); + } + else if (playerFrame != null) + { + playerFrame.AddToGUIUpdateList(); + } + else + { + menu.AddToGUIUpdateList(); + } + } + public override void Update(double deltaTime) { base.Update(deltaTime); @@ -944,9 +962,7 @@ namespace Barotrauma } else { - - menu.Update((float)deltaTime); - + menu.Update((float)deltaTime); } if (autoRestartTimer != 0.0f && autoRestartBox.Selected) diff --git a/Subsurface/Source/Screens/Screen.cs b/Subsurface/Source/Screens/Screen.cs index 0f69e280d..a57a06007 100644 --- a/Subsurface/Source/Screens/Screen.cs +++ b/Subsurface/Source/Screens/Screen.cs @@ -33,6 +33,10 @@ namespace Barotrauma get { return null; } } + public virtual void AddToGUIUpdateList() + { + } + public virtual void Update(double deltaTime) { } diff --git a/Subsurface/Source/Screens/ServerListScreen.cs b/Subsurface/Source/Screens/ServerListScreen.cs index 1226bc025..44ccd93d0 100644 --- a/Subsurface/Source/Screens/ServerListScreen.cs +++ b/Subsurface/Source/Screens/ServerListScreen.cs @@ -366,6 +366,11 @@ namespace Barotrauma spriteBatch.End(); } + public override void AddToGUIUpdateList() + { + menu.AddToGUIUpdateList(); + } + public override void Update(double deltaTime) { menu.Update((float)deltaTime); From 5b99f9e35e3d30c7fbdd713335e7a5f275ba85ac Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 16 Nov 2016 17:36:38 +0200 Subject: [PATCH 28/57] Saving server info box & traitor settings --- Subsurface/Source/Networking/GameServer.cs | 1 + .../Source/Networking/GameServerSettings.cs | 22 ++++++++++++++++--- Subsurface/Source/Screens/NetLobbyScreen.cs | 5 +++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 09c19dfcc..93b032205 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -1965,6 +1965,7 @@ namespace Barotrauma.Networking public override void Disconnect() { banList.Save(); + SaveSettings(); if (registeredToMaster && restClient != null) { diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index fee793ddb..090d47082 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -189,7 +189,8 @@ namespace Barotrauma.Networking { get { return banList; } } - + + [HasDefaultValue(true, true)] public bool AllowVoteKick { get; @@ -218,9 +219,16 @@ namespace Barotrauma.Networking doc.Root.SetAttributeValue("SubSelection", subSelectionMode.ToString()); doc.Root.SetAttributeValue("ModeSelection", modeSelectionMode.ToString()); + + doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString()); doc.Root.SetAttributeValue("MaxFileTransferDuration", FileStreamSender.MaxTransferDuration.TotalSeconds); - + + if (GameMain.NetLobbyScreen != null && GameMain.NetLobbyScreen.ServerMessage != null) + { + doc.Root.SetAttributeValue("ServerMessage", GameMain.NetLobbyScreen.ServerMessage.Text); + } + XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.NewLineOnAttributes = true; @@ -253,9 +261,17 @@ namespace Barotrauma.Networking modeSelectionMode = SelectionMode.Manual; Enum.TryParse(ToolBox.GetAttributeString(doc.Root, "ModeSelection", "Manual"), out modeSelectionMode); Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote; + + var traitorsEnabled = TraitorsEnabled; + Enum.TryParse(ToolBox.GetAttributeString(doc.Root, "TraitorsEnabled", "No"), out traitorsEnabled); + GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled); FileStreamSender.MaxTransferDuration = new TimeSpan(0,0,ToolBox.GetAttributeInt(doc.Root, "MaxFileTransferDuration", 150)); - + + if (GameMain.NetLobbyScreen != null && GameMain.NetLobbyScreen.ServerMessage != null) + { + GameMain.NetLobbyScreen.ServerMessage.Text = ToolBox.GetAttributeString(doc.Root, "ServerMessage", ""); + } showLogButton.Visible = SaveServerLogs; List monsterNames = Directory.GetDirectories("Content/Characters").ToList(); diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 074a20ec1..3db6bf0a7 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -51,6 +51,11 @@ namespace Barotrauma private GUITextBox serverMessage; + public GUITextBox ServerMessage + { + get { return serverMessage; } + } + public GUIListBox SubList { get { return subList; } From 3bd635e3d4d8c6612a1f57a1c9f8478d9bdc10a8 Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 16 Nov 2016 17:40:12 +0200 Subject: [PATCH 29/57] derp --- Subsurface/Source/Screens/NetLobbyScreen.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 908f3687c..b0f91015c 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -605,9 +605,8 @@ namespace Barotrauma return true; } - private void SetTraitorsEnabled(YesNoMaybe enabled) + public void SetTraitorsEnabled(YesNoMaybe enabled) { - if (GameMain.Server != null) GameMain.Server.TraitorsEnabled = enabled; (traitorProbabilityText as GUITextBlock).Text = enabled.ToString(); } From d37bad2d441a45cf103e7292eb018661c0309e81 Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 16 Nov 2016 18:32:18 +0200 Subject: [PATCH 30/57] Wire sections are updated after flipping, made the wire node list private and added public methods which ensure that sections are updated when modifying nodes --- Subsurface/Source/DebugConsole.cs | 2 +- .../Source/Items/Components/Signal/Wire.cs | 113 ++++++++++-------- Subsurface/Source/Map/MapEntity.cs | 3 +- Subsurface/Source/Map/Submarine.cs | 5 +- 4 files changed, 67 insertions(+), 56 deletions(-) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 63f4263ce..d18889d4e 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -627,7 +627,7 @@ namespace Barotrauma var wire = item.GetComponent(); if (wire == null) continue; - if (wire.Nodes.Any() && !wire.Connections.Any(c => c != null)) + if (wire.GetNodes().Count > 0 && !wire.Connections.Any(c => c != null)) { wire.Item.Drop(null); DebugConsole.NewMessage("Dropped wire (ID: "+wire.Item.ID+") - attached on wall but no connections found", Color.Orange); diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index b505011ca..412d16919 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -56,8 +56,7 @@ namespace Barotrauma.Items.Components static Sprite wireSprite; - public List Nodes; - + private List nodes; private List sections; Connection[] connections; @@ -83,7 +82,7 @@ namespace Barotrauma.Items.Components wireSprite.Depth = 0.85f; } - Nodes = new List(); + nodes = new List(); sections = new List(); connections = new Connection[2]; @@ -159,17 +158,17 @@ namespace Barotrauma.Items.Components if (newConnection.Item.Submarine == null) continue; - if (Nodes.Count > 0 && Nodes[0] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break; - if (Nodes.Count > 1 && Nodes[Nodes.Count-1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break; + if (nodes.Count > 0 && nodes[0] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break; + if (nodes.Count > 1 && nodes[nodes.Count-1] == newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition) break; if (i == 0) { - Nodes.Insert(0, newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition); + nodes.Insert(0, newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition); } else { - Nodes.Add(newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition); + nodes.Add(newConnection.Item.Position - newConnection.Item.Submarine.HiddenSubPosition); } @@ -192,7 +191,7 @@ namespace Barotrauma.Items.Components CleanNodes(); } - Drawable = Nodes.Any(); + Drawable = nodes.Any(); if (!loading) Item.NewComponentEvent(this, true, true); @@ -225,7 +224,7 @@ namespace Barotrauma.Items.Components public override void Update(float deltaTime, Camera cam) { - if (Nodes.Count == 0) return; + if (nodes.Count == 0) return; Submarine sub = null; if (connections[0] != null && connections[0].Item.Submarine != null) sub = connections[0].Item.Submarine; @@ -234,7 +233,6 @@ namespace Barotrauma.Items.Components if (item.Submarine != sub && Screen.Selected != GameMain.EditMapScreen) { ClearConnections(); - Nodes.Clear(); return; } @@ -245,9 +243,9 @@ namespace Barotrauma.Items.Components { if (character == Character.Controlled && character.SelectedConstruction != null) return false; - if (newNodePos!= Vector2.Zero && Nodes.Count>0 && Vector2.Distance(newNodePos, Nodes[Nodes.Count - 1]) > nodeDistance) + if (newNodePos!= Vector2.Zero && nodes.Count>0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) { - Nodes.Add(newNodePos); + nodes.Add(newNodePos); UpdateSections(); Drawable = true; @@ -259,9 +257,9 @@ namespace Barotrauma.Items.Components public override void SecondaryUse(float deltaTime, Character character = null) { - if (Nodes.Count > 1) + if (nodes.Count > 1) { - Nodes.RemoveAt(Nodes.Count - 1); + nodes.RemoveAt(nodes.Count - 1); UpdateSections(); item.NewComponentEvent(this, true, true); @@ -282,11 +280,22 @@ namespace Barotrauma.Items.Components if (item.IsSelected) MoveNodes(amount); } + public List GetNodes() + { + return new List(nodes); + } + + public void SetNodes(List nodes) + { + this.nodes = new List(nodes); + UpdateSections(); + } + public void MoveNodes(Vector2 amount) { - for (int i = 0; i < Nodes.Count; i++) + for (int i = 0; i < nodes.Count; i++) { - Nodes[i] += amount; + nodes[i] += amount; } UpdateSections(); } @@ -295,16 +304,16 @@ namespace Barotrauma.Items.Components { sections.Clear(); - for (int i = 0; i < Nodes.Count-1; i++) + for (int i = 0; i < nodes.Count-1; i++) { - sections.Add(new WireSection(Nodes[i], Nodes[i + 1])); + sections.Add(new WireSection(nodes[i], nodes[i + 1])); } Drawable = sections.Count > 0; } private void ClearConnections() { - Nodes.Clear(); + nodes.Clear(); sections.Clear(); for (int i = 0; i < 2; i++) @@ -348,14 +357,14 @@ namespace Barotrauma.Items.Components private void CleanNodes() { - for (int i = Nodes.Count - 2; i > 0; i--) + for (int i = nodes.Count - 2; i > 0; i--) { - if ((Nodes[i - 1].X == Nodes[i].X || Nodes[i - 1].Y == Nodes[i].Y) && - (Nodes[i + 1].X == Nodes[i].X || Nodes[i + 1].Y == Nodes[i].Y)) + if ((nodes[i - 1].X == nodes[i].X || nodes[i - 1].Y == nodes[i].Y) && + (nodes[i + 1].X == nodes[i].X || nodes[i + 1].Y == nodes[i].Y)) { - if (Vector2.Distance(Nodes[i - 1], Nodes[i]) == Vector2.Distance(Nodes[i + 1], Nodes[i])) + if (Vector2.Distance(nodes[i - 1], nodes[i]) == Vector2.Distance(nodes[i + 1], nodes[i])) { - Nodes.RemoveAt(i); + nodes.RemoveAt(i); } } } @@ -364,12 +373,12 @@ namespace Barotrauma.Items.Components do { removed = false; - for (int i = Nodes.Count - 2; i > 0; i--) + for (int i = nodes.Count - 2; i > 0; i--) { - if ((Nodes[i - 1].X == Nodes[i].X && Nodes[i + 1].X == Nodes[i].X) - || (Nodes[i - 1].Y == Nodes[i].Y && Nodes[i + 1].Y == Nodes[i].Y)) + if ((nodes[i - 1].X == nodes[i].X && nodes[i + 1].X == nodes[i].X) + || (nodes[i - 1].Y == nodes[i].Y && nodes[i + 1].Y == nodes[i].Y)) { - Nodes.RemoveAt(i); + nodes.RemoveAt(i); removed = true; } } @@ -414,11 +423,11 @@ namespace Barotrauma.Items.Components section.Draw(spriteBatch, item.Color, drawOffset, depth, 0.3f); } - if (IsActive && Vector2.Distance(newNodePos, Nodes[Nodes.Count - 1]) > nodeDistance) + if (IsActive && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) { WireSection.Draw( spriteBatch, - new Vector2(Nodes[Nodes.Count - 1].X, Nodes[Nodes.Count - 1].Y) + drawOffset, + new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset, new Vector2(newNodePos.X, newNodePos.Y) + drawOffset, item.Color * 0.5f, depth, @@ -428,9 +437,9 @@ namespace Barotrauma.Items.Components if (!editing || !PlayerInput.MouseInsideWindow || !GameMain.EditMapScreen.WiringMode) return; if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null) return; - for (int i = 0; i < Nodes.Count; i++) + for (int i = 0; i < nodes.Count; i++) { - Vector2 worldPos = Nodes[i]; + Vector2 worldPos = nodes[i]; if (item.Submarine != null) worldPos += item.Submarine.Position + item.Submarine.HiddenSubPosition; worldPos.Y = -worldPos.Y; @@ -458,7 +467,7 @@ namespace Barotrauma.Items.Components } else if (PlayerInput.RightButtonClicked()) { - Nodes.RemoveAt(i); + nodes.RemoveAt(i); break; } } @@ -483,7 +492,7 @@ namespace Barotrauma.Items.Components //if (item.Submarine != null) nodeWorldPos += item.Submarine.Position; - Nodes[(int)selectedNodeIndex] = nodeWorldPos; + nodes[(int)selectedNodeIndex] = nodeWorldPos; UpdateSections(); MapEntity.SelectEntity(item); @@ -498,23 +507,24 @@ namespace Barotrauma.Items.Components public override void FlipX() { - for (int i = 0; i < Nodes.Count; i++) + for (int i = 0; i < nodes.Count; i++) { - Nodes[i] = new Vector2(-Nodes[i].X, Nodes[i].Y); - } + nodes[i] = new Vector2(-nodes[i].X, nodes[i].Y); + } + UpdateSections(); } public override XElement Save(XElement parentElement) { XElement componentElement = base.Save(parentElement); - if (Nodes == null || Nodes.Count == 0) return componentElement; + if (nodes == null || nodes.Count == 0) return componentElement; - string[] nodeCoords = new string[Nodes.Count * 2]; - for (int i = 0; i < Nodes.Count; i++) + string[] nodeCoords = new string[nodes.Count * 2]; + for (int i = 0; i < nodes.Count; i++) { - nodeCoords[i * 2] = Nodes[i].X.ToString(CultureInfo.InvariantCulture); - nodeCoords[i * 2 + 1] = Nodes[i].Y.ToString(CultureInfo.InvariantCulture); + nodeCoords[i * 2] = nodes[i].X.ToString(CultureInfo.InvariantCulture); + nodeCoords[i * 2 + 1] = nodes[i].Y.ToString(CultureInfo.InvariantCulture); } componentElement.Add(new XAttribute("nodes", string.Join(";", nodeCoords))); @@ -546,10 +556,10 @@ namespace Barotrauma.Items.Components } catch { y = 0.0f; } - Nodes.Add(new Vector2(x, y)); + nodes.Add(new Vector2(x, y)); } - Drawable = Nodes.Any(); + Drawable = nodes.Any(); } @@ -562,11 +572,11 @@ namespace Barotrauma.Items.Components public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetBuffer message) { - message.Write((byte)Math.Min(Nodes.Count, 10)); - for (int i = 0; i < Math.Min(Nodes.Count,10); i++) + message.Write((byte)Math.Min(nodes.Count, 10)); + for (int i = 0; i < Math.Min(nodes.Count,10); i++) { - message.Write(Nodes[i].X); - message.Write(Nodes[i].Y); + message.Write(nodes[i].X); + message.Write(nodes[i].Y); } return true; @@ -574,7 +584,7 @@ namespace Barotrauma.Items.Components public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message, float sendingTime) { - Nodes.Clear(); + nodes.Clear(); List newNodes = new List(); int nodeCount = message.ReadByte(); @@ -585,9 +595,8 @@ namespace Barotrauma.Items.Components newNodes.Add(newNode); } - Nodes = newNodes; - - Drawable = Nodes.Any(); + SetNodes(newNodes); + Drawable = nodes.Any(); } } } diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 9ae6dea14..2b0a27b98 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -251,8 +251,7 @@ namespace Barotrauma var originalWire = ((Item)entitiesToClone[i]).GetComponent(); - cloneWire.Nodes = new List(originalWire.Nodes); - cloneWire.UpdateSections(); + cloneWire.SetNodes(originalWire.GetNodes()); for (int n = 0; n < 2; n++) { diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 76f0db368..9afaa82c8 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -1080,7 +1080,10 @@ namespace Barotrauma if (item.Submarine != this) continue; var wire = item.GetComponent(); - if (wire != null) wire.MoveNodes(-center); + if (wire != null) + { + wire.MoveNodes(-center); + } } for (int i = 0; i < MapEntity.mapEntityList.Count; i++) From 16cf9f6c812e56ae947686dd2b4d15b5e7ca99db Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 16 Nov 2016 20:14:14 +0200 Subject: [PATCH 31/57] Handling exceptions in particle update, gamesession UI order fix, traitor setting loading fix --- Subsurface/Source/GameSession/GameSession.cs | 2 +- Subsurface/Source/Networking/GameServerSettings.cs | 1 + Subsurface/Source/Particles/ParticleManager.cs | 13 ++++++++++++- Subsurface/Source/Screens/EditMapScreen.cs | 2 +- Subsurface/Source/Screens/GameScreen.cs | 5 +++-- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 681b47fd5..bd9ac9724 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -364,7 +364,7 @@ namespace Barotrauma { if (gameMode != null) gameMode.AddToGUIUpdateList(); - if (infoFrame != null) infoButton.AddToGUIUpdateList(); + if (infoFrame != null) infoFrame.AddToGUIUpdateList(); } public void Update(float deltaTime) diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index 090d47082..41432ee04 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -264,6 +264,7 @@ namespace Barotrauma.Networking var traitorsEnabled = TraitorsEnabled; Enum.TryParse(ToolBox.GetAttributeString(doc.Root, "TraitorsEnabled", "No"), out traitorsEnabled); + TraitorsEnabled = traitorsEnabled; GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled); FileStreamSender.MaxTransferDuration = new TimeSpan(0,0,ToolBox.GetAttributeInt(doc.Root, "MaxFileTransferDuration", 150)); diff --git a/Subsurface/Source/Particles/ParticleManager.cs b/Subsurface/Source/Particles/ParticleManager.cs index 6bec2ccc1..40adebd87 100644 --- a/Subsurface/Source/Particles/ParticleManager.cs +++ b/Subsurface/Source/Particles/ParticleManager.cs @@ -109,7 +109,18 @@ namespace Barotrauma.Particles { for (int i = 0; i < particleCount; i++) { - if (!particles[i].Update(deltaTime)) RemoveParticle(i); + bool remove = false; + try + { + remove = !particles[i].Update(deltaTime); + } + catch (Exception e) + { + DebugConsole.ThrowError("Particle update failed", e); + remove = true; + } + + if (remove) RemoveParticle(i); } } diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 6bae0e7ed..06f926915 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -657,7 +657,7 @@ namespace Barotrauma private GUIFrame CreateWiringPanel() { - GUIFrame frame = new GUIFrame(new Rectangle(0,0,50,300), null, Alignment.Right | Alignment.CenterY, GUI.Style); + GUIFrame frame = new GUIFrame(new Rectangle(0,0,65,300), null, Alignment.Right | Alignment.CenterY, GUI.Style); frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); GUIListBox listBox = new GUIListBox(Rectangle.Empty, GUI.Style, frame); diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index 4d31a95d7..660026c73 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -91,8 +91,6 @@ namespace Barotrauma public override void AddToGUIUpdateList() { - if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList(); - if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null) { if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem) @@ -100,6 +98,9 @@ namespace Barotrauma Character.Controlled.SelectedConstruction.AddToGUIUpdateList(); } } + + if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList(); + Character.AddAllToGUIUpdateList(); } From 1c206e1730cad9a3ed6d4aedb7bc3e5ef62cfefa Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 16 Nov 2016 21:40:13 +0200 Subject: [PATCH 32/57] Adding CrewManager to GUIUpdateList in single player mode --- Subsurface/Source/GameSession/GameSession.cs | 2 ++ Subsurface/Source/Networking/NetworkMember.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index bd9ac9724..7c984bed1 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -362,6 +362,8 @@ namespace Barotrauma public void AddToGUIUpdateList() { + if (CrewManager != null) CrewManager.AddToGUIUpdateList(); + if (gameMode != null) gameMode.AddToGUIUpdateList(); if (infoFrame != null) infoFrame.AddToGUIUpdateList(); diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 69b7474d4..21edefb26 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -343,8 +343,6 @@ namespace Barotrauma.Networking if (gameStarted && Screen.Selected == GameMain.GameScreen) { inGameHUD.AddToGUIUpdateList(); - - GameMain.GameSession.CrewManager.AddToGUIUpdateList(); } } From fac0c850a52525f13efa8077adf3d7eec6957deb Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 16 Nov 2016 21:30:49 -0300 Subject: [PATCH 33/57] Changes to collider behavior The collider now levitates above the ground, which makes small obstacles a non-issue. The raytest also helps the collider stick to staircases, so players don't jump off anymore. Crouching now changes the collider to a smaller version, so now there is actual functionality to crouching. I also removed the anchor from corpses of network players. I'm not entirely sure why this was done, but removing it doesn't seem to break anything. --- Subsurface/Content/Characters/Human/human.xml | 3 +- .../Content/Characters/Human/humanhusk.xml | 3 +- Subsurface/Content/Characters/Husk/husk.xml | 3 +- .../Animation/FishAnimController.cs | 40 +-- .../Animation/HumanoidAnimController.cs | 115 ++++---- .../Source/Characters/Animation/Ragdoll.cs | 249 +++++++++++++----- Subsurface/Source/GUI/GUI.cs | 2 + .../GameSession/GameModes/SinglePlayerMode.cs | 13 +- Subsurface/Source/GameSession/GameSession.cs | 4 +- Subsurface/Source/Networking/GameServer.cs | 2 +- 10 files changed, 287 insertions(+), 147 deletions(-) diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml index 6b70f9857..b190c755c 100644 --- a/Subsurface/Content/Characters/Human/human.xml +++ b/Subsurface/Content/Characters/Human/human.xml @@ -13,7 +13,8 @@ swimspeed="2.0" impacttolerance="7.5"> - + + diff --git a/Subsurface/Content/Characters/Human/humanhusk.xml b/Subsurface/Content/Characters/Human/humanhusk.xml index 73b9e9e13..52f0e389e 100644 --- a/Subsurface/Content/Characters/Human/humanhusk.xml +++ b/Subsurface/Content/Characters/Human/humanhusk.xml @@ -13,7 +13,8 @@ swimspeed="2.5" impacttolerance="7.5"> - + + diff --git a/Subsurface/Content/Characters/Husk/husk.xml b/Subsurface/Content/Characters/Husk/husk.xml index 177bd1407..0376a0262 100644 --- a/Subsurface/Content/Characters/Husk/husk.xml +++ b/Subsurface/Content/Characters/Husk/husk.xml @@ -14,7 +14,8 @@ walkspeed="1.2" swimspeed="2.5"> - + + diff --git a/Subsurface/Source/Characters/Animation/FishAnimController.cs b/Subsurface/Source/Characters/Animation/FishAnimController.cs index cb6a1c7ed..5547e9a70 100644 --- a/Subsurface/Source/Characters/Animation/FishAnimController.cs +++ b/Subsurface/Source/Characters/Animation/FishAnimController.cs @@ -51,17 +51,17 @@ namespace Barotrauma if (character.IsDead || character.IsUnconscious || stunTimer > 0.0f) { - collider.FarseerBody.FixedRotation = false; + Collider.FarseerBody.FixedRotation = false; if (character.IsRemotePlayer) { - MainLimb.pullJoint.WorldAnchorB = collider.SimPosition; + MainLimb.pullJoint.WorldAnchorB = Collider.SimPosition; MainLimb.pullJoint.Enabled = true; } else { - collider.LinearVelocity = (MainLimb.SimPosition - collider.SimPosition) * 60.0f; - collider.SmoothRotate(MainLimb.Rotation); + Collider.LinearVelocity = (MainLimb.SimPosition - Collider.SimPosition) * 60.0f; + Collider.SmoothRotate(MainLimb.Rotation); } if (stunTimer > 0) @@ -73,16 +73,16 @@ namespace Barotrauma } //re-enable collider - if (!collider.FarseerBody.Enabled) + if (!Collider.FarseerBody.Enabled) { var lowestLimb = FindLowestLimb(); - collider.SetTransform(new Vector2( - collider.SimPosition.X, - Math.Max(lowestLimb.SimPosition.Y + (collider.radius + collider.height / 2), collider.SimPosition.Y)), + Collider.SetTransform(new Vector2( + Collider.SimPosition.X, + Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)), 0.0f); - collider.FarseerBody.Enabled = true; + Collider.FarseerBody.Enabled = true; } ResetPullJoints(); @@ -96,20 +96,20 @@ namespace Barotrauma if (inWater) { - collider.FarseerBody.FixedRotation = false; + Collider.FarseerBody.FixedRotation = false; UpdateSineAnim(deltaTime); } else if (currentHull != null && CanEnterSubmarine) { - if (Math.Abs(MathUtils.GetShortestAngle(collider.Rotation, 0.0f)) > 0.001f) + if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, 0.0f)) > 0.001f) { //rotate collider back upright - collider.AngularVelocity = MathUtils.GetShortestAngle(collider.Rotation, 0.0f) * 60.0f; - collider.FarseerBody.FixedRotation = false; + Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f) * 60.0f; + Collider.FarseerBody.FixedRotation = false; } else { - collider.FarseerBody.FixedRotation = true; + Collider.FarseerBody.FixedRotation = true; } UpdateWalkAnim(deltaTime); @@ -172,7 +172,7 @@ namespace Barotrauma movement = TargetMovement*swimSpeed; MainLimb.pullJoint.Enabled = true; - MainLimb.pullJoint.WorldAnchorB = collider.SimPosition; + MainLimb.pullJoint.WorldAnchorB = Collider.SimPosition; if (movement.LengthSquared() < 0.00001f) return; @@ -180,12 +180,12 @@ namespace Barotrauma if (rotateTowardsMovement) { - collider.SmoothRotate(movementAngle, 25.0f); + Collider.SmoothRotate(movementAngle, 25.0f); MainLimb.body.SmoothRotate(movementAngle, 25.0f); } else { - collider.SmoothRotate(HeadAngle * Dir, 25.0f); + Collider.SmoothRotate(HeadAngle * Dir, 25.0f); MainLimb.body.SmoothRotate(HeadAngle * Dir, 25.0f); } @@ -216,7 +216,7 @@ namespace Barotrauma Limbs[i].body.ApplyForce(((limbPos - Limbs[i].SimPosition) * 3.0f - Limbs[i].LinearVelocity * 3.0f) * Limbs[i].Mass); } - collider.LinearVelocity = Vector2.Lerp(collider.LinearVelocity, movement, 0.5f); + Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement, 0.5f); floorY = Limbs[0].SimPosition.Y; } @@ -242,9 +242,9 @@ namespace Barotrauma MainLimb.body.SmoothRotate(mainLimbAngle * Dir, 50.0f); - collider.LinearVelocity = new Vector2( + Collider.LinearVelocity = new Vector2( movement.X, - collider.LinearVelocity.Y > 0.0f ? collider.LinearVelocity.Y * 0.5f : collider.LinearVelocity.Y); + Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y); MainLimb.MoveToPos(GetColliderBottom() + Vector2.UnitY * mainLimbHeight, 10.0f); diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index 838c86aec..e50fa3094 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -54,21 +54,25 @@ namespace Barotrauma public override void UpdateAnim(float deltaTime) { if (Frozen) return; - + + levitatingCollider = true; + ColliderIndex = Crouching ? 1 : 0; + if (!Crouching && ColliderIndex == 1) Crouching = true; + if (character.IsDead || character.IsUnconscious || stunTimer > 0.0f) { - collider.FarseerBody.FixedRotation = false; + Collider.FarseerBody.FixedRotation = false; - if (character.IsRemotePlayer) + /*if (character.IsRemotePlayer) { - MainLimb.pullJoint.WorldAnchorB = collider.SimPosition; + MainLimb.pullJoint.WorldAnchorB = Collider.SimPosition; MainLimb.pullJoint.Enabled = true; } else - { - collider.LinearVelocity = (GetLimb(LimbType.Waist).SimPosition - collider.SimPosition) * 20.0f; - collider.SmoothRotate(GetLimb(LimbType.Torso).Rotation); - } + {*/ + Collider.LinearVelocity = (GetLimb(LimbType.Waist).SimPosition - Collider.SimPosition) * 20.0f; + Collider.SmoothRotate(GetLimb(LimbType.Torso).Rotation); + //} if (stunTimer > 0) { @@ -90,39 +94,39 @@ namespace Barotrauma if (!character.IsRemotePlayer || true) { //re-enable collider - if (!collider.FarseerBody.Enabled) + if (!Collider.FarseerBody.Enabled) { var lowestLimb = FindLowestLimb(); - collider.SetTransform(new Vector2( - collider.SimPosition.X, - Math.Max(lowestLimb.SimPosition.Y + (collider.radius + collider.height / 2), collider.SimPosition.Y)), - collider.Rotation); + Collider.SetTransform(new Vector2( + Collider.SimPosition.X, + Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)), + Collider.Rotation); - collider.FarseerBody.Enabled = true; + Collider.FarseerBody.Enabled = true; } if (swimming) { - collider.FarseerBody.FixedRotation = false; + Collider.FarseerBody.FixedRotation = false; } - else if (!collider.FarseerBody.FixedRotation) + else if (!Collider.FarseerBody.FixedRotation) { - if (Math.Abs(MathUtils.GetShortestAngle(collider.Rotation, 0.0f)) > 0.001f) + if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, 0.0f)) > 0.001f) { //rotate collider back upright - collider.AngularVelocity = MathUtils.GetShortestAngle(collider.Rotation, 0.0f) * 10.0f; - collider.FarseerBody.FixedRotation = false; + Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f) * 10.0f; + Collider.FarseerBody.FixedRotation = false; } else { - collider.FarseerBody.FixedRotation = true; + Collider.FarseerBody.FixedRotation = true; } } } else { - collider.FarseerBody.Enabled = false; + Collider.FarseerBody.Enabled = false; } if (character.LockHands) @@ -165,6 +169,7 @@ namespace Barotrauma switch (Anim) { case Animation.Climbing: + levitatingCollider = false; UpdateClimbing(); break; case Animation.UsingConstruction: @@ -210,7 +215,7 @@ namespace Barotrauma } aiming = false; - if (character.IsRemotePlayer && GameMain.Server == null) collider.LinearVelocity = Vector2.Zero; + if (character.IsRemotePlayer && GameMain.Server == null) Collider.LinearVelocity = Vector2.Zero; } @@ -240,12 +245,12 @@ namespace Barotrauma { TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.5f, 1.5f), TargetMovement.Y); - if ((TargetMovement.X > 0.0f && stairs.StairDirection == Direction.Right) || + /*if ((TargetMovement.X > 0.0f && stairs.StairDirection == Direction.Right) || TargetMovement.X < 0.0f && stairs.StairDirection == Direction.Left) { TargetMovement *= 1.7f; //walkCycleSpeed *= 1.0f; - } + }*/ } Vector2 colliderPos = GetColliderBottom(); @@ -308,14 +313,14 @@ namespace Barotrauma if (onGround && (!character.IsRemotePlayer || GameMain.Server != null)) { //move slower if collider isn't upright - float rotationFactor = (float)Math.Abs(Math.Cos(collider.Rotation)); + float rotationFactor = (float)Math.Abs(Math.Cos(Collider.Rotation)); - collider.LinearVelocity = new Vector2( + Collider.LinearVelocity = new Vector2( movement.X * rotationFactor, - collider.LinearVelocity.Y > 0.0f ? collider.LinearVelocity.Y * 0.5f : collider.LinearVelocity.Y); + Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y); } - ClimbOverObstacles(); + //ClimbOverObstacles(); getUpSpeed = getUpSpeed * Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f); @@ -323,8 +328,8 @@ namespace Barotrauma head.pullJoint.Enabled = true; waist.pullJoint.Enabled = true; - collider.FarseerBody.Friction = 0.05f; - collider.FarseerBody.Restitution = 0.05f; + Collider.FarseerBody.Friction = 0.05f; + Collider.FarseerBody.Restitution = 0.05f; if (stairs != null) { @@ -486,11 +491,11 @@ namespace Barotrauma private void ClimbOverObstacles() { - if (collider.FarseerBody.ContactList == null || Math.Abs(movement.X) < 0.01f) return; + if (Collider.FarseerBody.ContactList == null || Math.Abs(movement.X) < 0.01f) return; //check if the collider is touching a suitable obstacle to climb over Vector2? handle = null; - FarseerPhysics.Dynamics.Contacts.ContactEdge ce = collider.FarseerBody.ContactList; + FarseerPhysics.Dynamics.Contacts.ContactEdge ce = Collider.FarseerBody.ContactList; while (ce != null && ce.Contact != null) { if (ce.Contact.Enabled && ce.Contact.IsTouching && ce.Contact.FixtureA.CollisionCategories.HasFlag(Physics.CollisionWall)) @@ -500,7 +505,7 @@ namespace Barotrauma ce.Contact.GetWorldManifold(out contactNormal, out contactPos); //only climb if moving towards the obstacle - if (Math.Sign(contactPos[0].X - collider.SimPosition.X) == Math.Sign(movement.X) && + if (Math.Sign(contactPos[0].X - Collider.SimPosition.X) == Math.Sign(movement.X) && (handle == null || contactPos[0].Y > ((Vector2)handle).Y)) { handle = contactPos[0]; @@ -516,7 +521,7 @@ namespace Barotrauma //the contact point should be higher than the bottom of the collider if (((Vector2)handle).Y < colliderBottomY + 0.01f || - ((Vector2)handle).Y > collider.SimPosition.Y) return; + ((Vector2)handle).Y > Collider.SimPosition.Y) return; //find the height of the floor below the torso //(if moving towards towards an obstacle that's low enough to climb over, the torso should be above it) @@ -525,7 +530,7 @@ namespace Barotrauma if (obstacleY > colliderBottomY) { //higher vertical velocity for taller obstacles - collider.LinearVelocity += Vector2.UnitY * (((Vector2)handle).Y - colliderBottomY + 0.01f) * 50; + Collider.LinearVelocity += Vector2.UnitY * (((Vector2)handle).Y - colliderBottomY + 0.01f) * 50; onGround = true; } } @@ -543,7 +548,7 @@ namespace Barotrauma if (currentHull != null && (currentHull.Rect.Y - currentHull.Surface > 50.0f)) { - surfaceLimiter = (ConvertUnits.ToDisplayUnits(collider.SimPosition.Y + 0.4f) - surfaceY); + surfaceLimiter = (ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 0.4f) - surfaceY); surfaceLimiter = Math.Max(1.0f, surfaceLimiter); if (surfaceLimiter > 50.0f) return; } @@ -554,7 +559,7 @@ namespace Barotrauma Limb leftFoot = GetLimb(LimbType.LeftFoot); Limb rightFoot = GetLimb(LimbType.RightFoot); - float rotation = MathHelper.WrapAngle(collider.Rotation); + float rotation = MathHelper.WrapAngle(Collider.Rotation); rotation = MathHelper.ToDegrees(rotation); if (rotation < 0.0f) rotation += 360; @@ -574,7 +579,7 @@ namespace Barotrauma if (!aiming) { float newRotation = MathUtils.VectorToAngle(TargetMovement) - MathHelper.PiOver2; - collider.SmoothRotate(newRotation, 5.0f); + Collider.SmoothRotate(newRotation, 5.0f); //torso.body.SmoothRotate(newRotation); } @@ -589,12 +594,12 @@ namespace Barotrauma TargetMovement = new Vector2(0.0f, -0.1f); float newRotation = MathUtils.VectorToAngle(diff); - collider.SmoothRotate(newRotation, 5.0f); + Collider.SmoothRotate(newRotation, 5.0f); } } - torso.body.SmoothRotate(collider.Rotation); - torso.body.MoveToPos(collider.SimPosition + new Vector2((float)Math.Sin(-collider.Rotation), (float)Math.Cos(-collider.Rotation))*0.4f, 5.0f); + torso.body.SmoothRotate(Collider.Rotation); + torso.body.MoveToPos(Collider.SimPosition + new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation))*0.4f, 5.0f); if (TargetMovement == Vector2.Zero) return; @@ -626,11 +631,11 @@ namespace Barotrauma if (!character.IsRemotePlayer || GameMain.Server != null) { - collider.LinearVelocity = Vector2.Lerp(collider.LinearVelocity, movement * swimSpeed, movementLerp); + Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement * swimSpeed, movementLerp); } walkPos += movement.Length() * 0.15f; - footPos = collider.SimPosition - new Vector2((float)Math.Sin(-collider.Rotation), (float)Math.Cos(-collider.Rotation)) * 0.4f; + footPos = Collider.SimPosition - new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * 0.4f; var rightThigh = GetLimb(LimbType.RightThigh); var leftThigh = GetLimb(LimbType.LeftThigh); @@ -641,7 +646,7 @@ namespace Barotrauma Vector2 transformedFootPos = new Vector2((float)Math.Sin(walkPos) * 0.5f, 0.0f); transformedFootPos = Vector2.Transform( transformedFootPos, - Matrix.CreateRotationZ(collider.Rotation)); + Matrix.CreateRotationZ(Collider.Rotation)); MoveLimb(rightFoot, footPos - transformedFootPos, 1.0f); MoveLimb(leftFoot, footPos + transformedFootPos, 1.0f); @@ -742,20 +747,20 @@ namespace Barotrauma ladderSimPos += character.SelectedConstruction.Submarine.SimPosition - currentHull.Submarine.SimPosition; } - MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, collider.SimPosition.Y + 0.7f), 10.5f); - MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, collider.SimPosition.Y+0.5f), 10.5f); - MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, collider.SimPosition.Y+0.4f), 10.5f); + MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f), 10.5f); + MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y+0.5f), 10.5f); + MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y+0.4f), 10.5f); if (!character.IsRemotePlayer) { - collider.MoveToPos(new Vector2(ladderSimPos.X - 0.2f * Dir, collider.SimPosition.Y), 10.5f); + Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.2f * Dir, Collider.SimPosition.Y), 10.5f); } bool slide = targetMovement.Y < -1.1f; Vector2 handPos = new Vector2( ladderSimPos.X, - collider.SimPosition.Y + 0.6f + movement.Y * 0.1f - ladderSimPos.Y); + Collider.SimPosition.Y + 0.6f + movement.Y * 0.1f - ladderSimPos.Y); handPos.Y = Math.Min(-0.5f, handPos.Y); @@ -774,7 +779,7 @@ namespace Barotrauma Vector2 footPos = new Vector2( handPos.X - Dir * 0.05f, - collider.SimPosition.Y + 0.7f - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); + Collider.SimPosition.Y + 0.7f - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); //if (movement.Y < 0) footPos.Y += 0.05f; @@ -805,7 +810,7 @@ namespace Barotrauma //if (climbForce.Y > 0.5f) climbForce.Y = Math.Max(climbForce.Y, 1.3f); //apply forces to the collider to move the Character up/down - collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * collider.Mass); + Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass); head.body.SmoothRotate(0.0f); if (!character.SelectedConstruction.Prefab.Triggers.Any()) @@ -907,10 +912,10 @@ namespace Barotrauma } } - float dist = Vector2.Distance(target.SimPosition, collider.SimPosition); + float dist = Vector2.Distance(target.SimPosition, Collider.SimPosition); //limit movement if moving away from the target - if (Vector2.Dot(target.SimPosition - collider.SimPosition, targetMovement)<0) + if (Vector2.Dot(target.SimPosition - Collider.SimPosition, targetMovement)<0) { targetMovement *= MathHelper.Clamp(2.0f - dist, 0.0f, 1.0f); } @@ -1148,8 +1153,8 @@ namespace Barotrauma float angle = flipAngle ? -limb.body.Rotation : limb.body.Rotation; if (wrapAngle) angle = MathUtils.WrapAnglePi(angle); - - TrySetLimbPosition(limb, collider.SimPosition, position); + + TrySetLimbPosition(limb, Collider.SimPosition, position); limb.body.SetTransform(limb.body.SimPosition, angle); } diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index aee5ff37d..429d39be9 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -35,7 +35,7 @@ namespace Barotrauma { l.body.PhysEnabled = !frozen; } - collider.PhysEnabled = !frozen; + Collider.PhysEnabled = !frozen; } } @@ -77,13 +77,45 @@ namespace Barotrauma public Direction TargetDir; - protected PhysicsBody collider; - + protected List collider; + protected int colliderIndex = 0; + public PhysicsBody Collider { get { - return collider; + return collider[colliderIndex]; + } + } + + public int ColliderIndex + { + get + { + return colliderIndex; + } + set + { + if (value == colliderIndex) return; + if (value >= collider.Count) return; + if (collider[colliderIndex].height f.CollisionCategories.HasFlag(Physics.CollisionWall))) return; + } + collider[value].LinearVelocity = collider[colliderIndex].LinearVelocity; + collider[value].AngularVelocity = collider[colliderIndex].AngularVelocity; + Vector2 pos = collider[colliderIndex].SimPosition; + pos.Y -= collider[colliderIndex].height * 0.5f; + pos.Y += collider[value].height * 0.5f; + collider[value].SetTransform(pos, collider[colliderIndex].Rotation); + collider[value].PhysEnabled = !frozen; + collider[value].Enabled = !simplePhysicsEnabled; + collider[colliderIndex].PhysEnabled = false; + colliderIndex = value; } } @@ -109,8 +141,8 @@ namespace Barotrauma get { return character.Submarine == null ? - ConvertUnits.ToDisplayUnits(collider.SimPosition) : - ConvertUnits.ToDisplayUnits(collider.SimPosition) + character.Submarine.Position; + ConvertUnits.ToDisplayUnits(Collider.SimPosition) : + ConvertUnits.ToDisplayUnits(Collider.SimPosition) + character.Submarine.Position; } } @@ -137,7 +169,7 @@ namespace Barotrauma { foreach (Limb limb in Limbs) { - limb.body.SetTransform(collider.SimPosition, collider.Rotation); + limb.body.SetTransform(Collider.SimPosition, Collider.Rotation); } } } @@ -207,7 +239,7 @@ namespace Barotrauma { limb.body.Submarine = currSubmarine; } - collider.Submarine = currSubmarine; + Collider.Submarine = currSubmarine; } } @@ -266,7 +298,9 @@ namespace Barotrauma ImpactTolerance = ToolBox.GetAttributeFloat(element, "impacttolerance", 50.0f); CanEnterSubmarine = ToolBox.GetAttributeBool(element, "canentersubmarine", true); - + + collider = new List(); + foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString()) @@ -287,24 +321,28 @@ namespace Barotrauma break; case "collider": - collider = new PhysicsBody(subElement, scale); - collider.FarseerBody.FixedRotation = true; + collider.Add(new PhysicsBody(subElement, scale)); + collider[collider.Count - 1].FarseerBody.FixedRotation = true; + collider[collider.Count - 1].CollisionCategories = Physics.CollisionCharacter; + collider[collider.Count - 1].FarseerBody.AngularDamping = 5.0f; + collider[collider.Count - 1].FarseerBody.FixedRotation = true; + collider[collider.Count - 1].FarseerBody.OnCollision += OnLimbCollision; + if (collider.Count > 1) collider[collider.Count - 1].PhysEnabled = false; break; } } - if (collider == null) + if (collider[0] == null) { DebugConsole.ThrowError("No collider configured for ''"+character.Name+"''!"); - collider = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f); - collider.BodyType = BodyType.Dynamic; + collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f); + collider[0].BodyType = BodyType.Dynamic; + collider[0].CollisionCategories = Physics.CollisionCharacter; + collider[0].FarseerBody.AngularDamping = 5.0f; + collider[0].FarseerBody.FixedRotation = true; + collider[0].FarseerBody.OnCollision += OnLimbCollision; } - collider.CollisionCategories = Physics.CollisionCharacter; - collider.FarseerBody.AngularDamping = 5.0f; - collider.FarseerBody.FixedRotation = true; - collider.FarseerBody.OnCollision += OnLimbCollision; - UpdateCollisionCategories(); foreach (var joint in limbJoints) @@ -431,7 +469,7 @@ namespace Barotrauma //4. contact points is above the bottom half of the collider Vector2 normal; FarseerPhysics.Common.FixedArray2 points; contact.GetWorldManifold(out normal, out points); - if (points[0].Y > collider.SimPosition.Y) return false; + if (points[0].Y > Collider.SimPosition.Y) return false; //5. in water if (inWater && targetMovement.Y < 0.5f) return false; @@ -475,14 +513,14 @@ namespace Barotrauma limb.HitSound.Play(volume, impact * 100.0f, limb.WorldPosition); } } - else if (f1.Body == collider.FarseerBody) + else if (f1.Body == Collider.FarseerBody) { if (!character.IsRemotePlayer || GameMain.Server != null) { if (impact > ImpactTolerance) { character.AddDamage(CauseOfDeath.Damage, impact - ImpactTolerance, null); - SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, strongestImpact, collider); + SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, strongestImpact, Collider); strongestImpact = Math.Max(strongestImpact, impact - ImpactTolerance); } } @@ -495,7 +533,7 @@ namespace Barotrauma { if (simplePhysicsEnabled) return; - collider.UpdateDrawPosition(); + Collider.UpdateDrawPosition(); foreach (Limb limb in Limbs) { @@ -522,7 +560,7 @@ namespace Barotrauma limb.body.DebugDraw(spriteBatch, inWater ? Color.Cyan : Color.White); } - collider.DebugDraw(spriteBatch, inWater ? Color.SkyBlue : Color.Gray); + Collider.DebugDraw(spriteBatch, inWater ? Color.SkyBlue : Color.Gray); foreach (RevoluteJoint joint in limbJoints) { @@ -568,8 +606,8 @@ namespace Barotrauma if (ignorePlatforms) { GUI.DrawLine(spriteBatch, - new Vector2(collider.DrawPosition.X, -collider.DrawPosition.Y), - new Vector2(collider.DrawPosition.X, -collider.DrawPosition.Y + 50), + new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), + new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y + 50), Color.Orange, 0, 5); } } @@ -718,7 +756,7 @@ namespace Barotrauma //character.Stun = 0.1f; character.DisableImpactDamageTimer = 0.25f; - SetPosition(collider.SimPosition + moveAmount); + SetPosition(Collider.SimPosition + moveAmount); character.CursorPosition += moveAmount; } @@ -732,7 +770,7 @@ namespace Barotrauma wall | Physics.CollisionProjectile | Physics.CollisionStairs : wall | Physics.CollisionProjectile | Physics.CollisionPlatform | Physics.CollisionStairs; - collider.CollidesWith = collisionCategory; + Collider.CollidesWith = collisionCategory; foreach (Limb limb in Limbs) { @@ -749,19 +787,21 @@ namespace Barotrauma } } + protected bool levitatingCollider = true; + public void Update(Camera cam, float deltaTime) { if (!character.Enabled || Frozen) return; UpdateNetPlayerPosition(deltaTime); CheckDistFromCollider(); - + Vector2 flowForce = Vector2.Zero; FindHull(); splashSoundTimer -= deltaTime; - + //ragdoll isn't in any room -> it's in the water if (currentHull == null) { @@ -779,41 +819,41 @@ namespace Barotrauma float floorY = GetFloorY(); - if (currentHull.Volume > currentHull.FullVolume * 0.95f || - (waterSurface - floorY > HeadPosition * 0.95f && collider.SimPosition.Y < waterSurface)) - inWater = true; + if (currentHull.Volume > currentHull.FullVolume * 0.95f || + (waterSurface - floorY > HeadPosition * 0.95f && Collider.SimPosition.Y < waterSurface)) + inWater = true; } if (flowForce.LengthSquared() > 0.001f) { - collider.ApplyForce(flowForce); + Collider.ApplyForce(flowForce); } - if (currentHull==null || - currentHull.Volume > currentHull.FullVolume * 0.95f || - ConvertUnits.ToSimUnits(currentHull.Surface) > collider.SimPosition.Y) + if (currentHull == null || + currentHull.Volume > currentHull.FullVolume * 0.95f || + ConvertUnits.ToSimUnits(currentHull.Surface) > Collider.SimPosition.Y) { - collider.ApplyWaterForces(); + Collider.ApplyWaterForces(); } - - + + foreach (Limb limb in Limbs) { //find the room which the limb is in //the room where the ragdoll is in is used as the "guess", meaning that it's checked first Hull limbHull = currentHull == null ? null : Hull.FindHull(limb.WorldPosition, currentHull); - + bool prevInWater = limb.inWater; limb.inWater = false; if (limbHull == null) - { + { //limb isn't in any room -> it's in the water limb.inWater = true; } else if (limbHull.Volume > 0.0f && Submarine.RectContains(limbHull.Rect, limb.Position)) { - if (limb.Position.Y < limbHull.Surface) + if (limb.Position.Y < limbHull.Surface) { limb.inWater = true; @@ -829,7 +869,7 @@ namespace Barotrauma headInWater = true; } } - //the limb has gone through the surface of the water + //the limb has gone through the surface of the water if (Math.Abs(limb.LinearVelocity.Y) > 5.0f && limb.inWater != prevInWater) { @@ -838,14 +878,14 @@ namespace Barotrauma new Vector2(limb.Position.X, limbHull.Surface) + limbHull.Submarine.Position, new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 20.0f)), 0.0f, limbHull); - + GameMain.ParticleManager.CreateParticle("bubbles", - new Vector2(limb.Position.X, limbHull.Surface) + limbHull.Submarine.Position, - limb.LinearVelocity*0.001f, + new Vector2(limb.Position.X, limbHull.Surface) + limbHull.Submarine.Position, + limb.LinearVelocity * 0.001f, 0.0f, limbHull); //if the Character dropped into water, create a wave - if (limb.LinearVelocity.Y<0.0f) + if (limb.LinearVelocity.Y < 0.0f) { if (splashSoundTimer <= 0.0f) { @@ -866,13 +906,12 @@ namespace Barotrauma limb.Update(deltaTime); } - - + bool onStairs = stairs != null; stairs = null; - var contacts = collider.FarseerBody.ContactList; - while (collider.FarseerBody.Enabled && contacts != null && contacts.Contact != null) + var contacts = Collider.FarseerBody.ContactList; + while (Collider.FarseerBody.Enabled && contacts != null && contacts.Contact != null) { if (contacts.Contact.Enabled && contacts.Contact.IsTouching) { @@ -907,7 +946,7 @@ namespace Barotrauma //} - if (points[0].Y < collider.SimPosition.Y) + if (points[0].Y < Collider.SimPosition.Y) { floorY = Math.Max(floorY, points[0].Y); @@ -931,12 +970,86 @@ namespace Barotrauma onFloorTimer -= deltaTime; } + Vector2 rayStart = Collider.SimPosition; + Vector2 rayEnd = rayStart; + rayEnd.Y -= Collider.height * 0.5f + Collider.radius + ConvertUnits.ToSimUnits(55.0f); + var lowestLimb = FindLowestLimb(); + + //TODO: use something like this instead of lowest limb, whenever we get to that + //float footY = Collider.SimPosition.Y + Collider.height * 0.5f + Collider.radius + ConvertUnits.ToSimUnits(45.0f); + + if (!inWater && levitatingCollider) + { + float closestFraction = 1.0f; + Fixture closestFixture = null; + GameMain.World.RayCast((fixture, point, normal, fraction) => + { + switch (fixture.CollisionCategories) + { + case Physics.CollisionStairs: + Structure structure = fixture.Body.UserData as Structure; + if (lowestLimb.Position.Y 0.01f && Collider.SimPosition.Y allowedDist*allowedDist) + if (Vector2.DistanceSquared(Collider.SimPosition, MainLimb.SimPosition) > allowedDist*allowedDist) { if (!collisionsDisabled) { @@ -1057,7 +1170,7 @@ namespace Barotrauma else if (collisionsDisabled) { //set the position of the ragdoll to make sure limbs don't get stuck inside walls when re-enabling collisions - SetPosition(collider.SimPosition, true); + SetPosition(Collider.SimPosition, true); UpdateCollisionCategories(); collisionsDisabled = false; @@ -1096,8 +1209,8 @@ namespace Barotrauma { if (character.MemPos.Count > 0) { - collider.LinearVelocity = Vector2.Zero; - collider.CorrectPosition(character.MemPos, deltaTime, out overrideTargetMovement); + Collider.LinearVelocity = Vector2.Zero; + Collider.CorrectPosition(character.MemPos, deltaTime, out overrideTargetMovement); } } } @@ -1133,10 +1246,12 @@ namespace Barotrauma public Vector2 GetColliderBottom() { - float halfHeight = collider.height / 2 + collider.radius; + float halfHeight = Collider.height * 0.5f + Collider.radius; - return collider.SimPosition + - new Vector2((float)Math.Sin(collider.Rotation), -(float)Math.Cos(collider.Rotation)) * halfHeight; + Vector2 offset = Vector2.Zero; + offset.Y = ConvertUnits.ToSimUnits(-45.0f); + return Collider.SimPosition + offset + + new Vector2((float)Math.Sin(Collider.Rotation), -(float)Math.Cos(Collider.Rotation)) * halfHeight; } public Limb FindLowestLimb() @@ -1161,8 +1276,10 @@ namespace Barotrauma } Limbs = null; - collider.Remove(); - collider = null; + foreach (PhysicsBody b in collider) + { + b.Remove(); + } foreach (RevoluteJoint joint in limbJoints) { diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index 18b8f3c59..99040670c 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -200,6 +200,8 @@ namespace Barotrauma GameMain.NetworkMember = null; } + CoroutineManager.StopCoroutines("EndCinematic"); + GameMain.GameSession = null; GameMain.MainMenuScreen.Select(); diff --git a/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs b/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs index a176b3ac3..5d26ecd4f 100644 --- a/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs +++ b/Subsurface/Source/GameSession/GameModes/SinglePlayerMode.cs @@ -215,6 +215,17 @@ namespace Barotrauma endShiftButton.Draw(spriteBatch); } + public override void AddToGUIUpdateList() + { + if (!isRunning) return; + + base.AddToGUIUpdateList(); + + CrewManager.AddToGUIUpdateList(); + + endShiftButton.AddToGUIUpdateList(); + } + public override void Update(float deltaTime) { if (!isRunning) return; @@ -357,7 +368,7 @@ namespace Barotrauma SoundPlayer.OverrideMusicType = CrewManager.characters.Any(c => !c.IsDead) ? "endshift" : "crewdead"; - CoroutineManager.StartCoroutine(EndCinematic(cinematic)); + CoroutineManager.StartCoroutine(EndCinematic(cinematic),"EndCinematic"); return true; } diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index 681b47fd5..57703053f 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -364,7 +364,9 @@ namespace Barotrauma { if (gameMode != null) gameMode.AddToGUIUpdateList(); - if (infoFrame != null) infoButton.AddToGUIUpdateList(); + infoButton.AddToGUIUpdateList(); + + if (infoFrame != null) infoFrame.AddToGUIUpdateList(); } public void Update(float deltaTime) diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 98fc5b4b4..3fd4f9ba7 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -1165,7 +1165,7 @@ namespace Barotrauma.Networking } } - CoroutineManager.StartCoroutine(EndCinematic()); + CoroutineManager.StartCoroutine(EndCinematic(),"EndCinematic"); } public IEnumerable EndCinematic() From b68be44864f301fee9eede518cc892680c1889e3 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 16 Nov 2016 21:44:08 -0300 Subject: [PATCH 34/57] Fixed ragdoll position not being offset on ladder --- .../Characters/Animation/HumanoidAnimController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index e50fa3094..6a1b719c6 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -747,9 +747,9 @@ namespace Barotrauma ladderSimPos += character.SelectedConstruction.Submarine.SimPosition - currentHull.Submarine.SimPosition; } - MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f), 10.5f); - MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y+0.5f), 10.5f); - MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y+0.4f), 10.5f); + MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); + MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y+ 0.5f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); + MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y+ 0.4f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); if (!character.IsRemotePlayer) { @@ -762,7 +762,7 @@ namespace Barotrauma ladderSimPos.X, Collider.SimPosition.Y + 0.6f + movement.Y * 0.1f - ladderSimPos.Y); - handPos.Y = Math.Min(-0.5f, handPos.Y); + handPos.Y = Math.Min(-0.5f, handPos.Y) + ConvertUnits.ToSimUnits(-45.0f); MoveLimb(leftHand, new Vector2(handPos.X, @@ -779,7 +779,7 @@ namespace Barotrauma Vector2 footPos = new Vector2( handPos.X - Dir * 0.05f, - Collider.SimPosition.Y + 0.7f - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); + Collider.SimPosition.Y + 0.7f + ConvertUnits.ToSimUnits(-45.0f) - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); //if (movement.Y < 0) footPos.Y += 0.05f; From c25ea7c75f22458376abdb6751074306eba84d5e Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Wed, 16 Nov 2016 22:06:10 -0300 Subject: [PATCH 35/57] Fixed collider not taking previous Submarine value on index change --- Subsurface/Source/Characters/Animation/Ragdoll.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 429d39be9..554f84770 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -114,6 +114,7 @@ namespace Barotrauma collider[value].SetTransform(pos, collider[colliderIndex].Rotation); collider[value].PhysEnabled = !frozen; collider[value].Enabled = !simplePhysicsEnabled; + collider[value].Submarine = collider[colliderIndex].Submarine; collider[colliderIndex].PhysEnabled = false; colliderIndex = value; } From adece2007223836e1d3778e0d6b02b03437bbf81 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 17 Nov 2016 18:06:43 +0200 Subject: [PATCH 36/57] Collider height from floor can be changed in the xml files, moved human collider a bit higher to allow climbing over roughly waist-high obstacles --- Subsurface/Content/Characters/Human/human.xml | 7 ++++--- Subsurface/Source/Characters/Animation/Ragdoll.cs | 11 ++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml index b190c755c..e62c83b47 100644 --- a/Subsurface/Content/Characters/Human/human.xml +++ b/Subsurface/Content/Characters/Human/human.xml @@ -11,10 +11,11 @@ thightorque="-5.0" walkspeed="1.5" swimspeed="2.0" - impacttolerance="7.5"> + impacttolerance="7.5" + colliderheightfromfloor="55"> - - + + diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 554f84770..a41292c3b 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -70,6 +70,8 @@ namespace Barotrauma protected bool inWater, headInWater; public bool onGround; private bool ignorePlatforms; + + private float colliderHeightFromFloor; protected Structure stairs; @@ -101,7 +103,7 @@ namespace Barotrauma if (collider[colliderIndex].height f.CollisionCategories.HasFlag(Physics.CollisionWall))) return; @@ -300,6 +302,9 @@ namespace Barotrauma CanEnterSubmarine = ToolBox.GetAttributeBool(element, "canentersubmarine", true); + colliderHeightFromFloor = ToolBox.GetAttributeFloat(element, "colliderheightfromfloor", 45.0f); + colliderHeightFromFloor = ConvertUnits.ToSimUnits(colliderHeightFromFloor); + collider = new List(); foreach (XElement subElement in element.Elements()) @@ -994,7 +999,7 @@ namespace Barotrauma break; case Physics.CollisionPlatform: Structure platform = fixture.Body.UserData as Structure; - if (IgnorePlatforms || lowestLimb.Position.Y < platform.Rect.Y) return -1; + if (IgnorePlatforms || lowestLimb.Position.Y < platform.Rect.Y-16) return -1; break; case Physics.CollisionWall: break; @@ -1027,7 +1032,7 @@ namespace Barotrauma } float tfloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction; - float targetY = tfloorY + Collider.height * 0.5f + Collider.radius + ConvertUnits.ToSimUnits(45.0f); + float targetY = tfloorY + Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor; if (Math.Abs(Collider.SimPosition.Y - targetY) > 0.01f && Collider.SimPosition.Y Date: Thu, 17 Nov 2016 19:06:12 +0200 Subject: [PATCH 37/57] Added colliderHeightFromFloor to a few places I missed, some more human collider tweaking, correcting collider y position faster --- Subsurface/Content/Characters/Human/human.xml | 8 ++++---- .../Characters/Animation/HumanoidAnimController.cs | 12 ++++++------ Subsurface/Source/Characters/Animation/Ragdoll.cs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml index e62c83b47..fb1644409 100644 --- a/Subsurface/Content/Characters/Human/human.xml +++ b/Subsurface/Content/Characters/Human/human.xml @@ -11,11 +11,11 @@ thightorque="-5.0" walkspeed="1.5" swimspeed="2.0" - impacttolerance="7.5" - colliderheightfromfloor="55"> + colliderheightfromfloor="55" + impacttolerance="7.5"> - - + + diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index 6a1b719c6..a2c04379f 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -746,10 +746,10 @@ namespace Barotrauma { ladderSimPos += character.SelectedConstruction.Submarine.SimPosition - currentHull.Submarine.SimPosition; } - - MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); - MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y+ 0.5f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); - MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y+ 0.4f + ConvertUnits.ToSimUnits(-45.0f)), 10.5f); + + MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f - colliderHeightFromFloor), 10.5f); + MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.5f - colliderHeightFromFloor), 10.5f); + MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y + 0.4f - colliderHeightFromFloor), 10.5f); if (!character.IsRemotePlayer) { @@ -762,7 +762,7 @@ namespace Barotrauma ladderSimPos.X, Collider.SimPosition.Y + 0.6f + movement.Y * 0.1f - ladderSimPos.Y); - handPos.Y = Math.Min(-0.5f, handPos.Y) + ConvertUnits.ToSimUnits(-45.0f); + handPos.Y = Math.Min(-0.5f, handPos.Y) - colliderHeightFromFloor; MoveLimb(leftHand, new Vector2(handPos.X, @@ -779,7 +779,7 @@ namespace Barotrauma Vector2 footPos = new Vector2( handPos.X - Dir * 0.05f, - Collider.SimPosition.Y + 0.7f + ConvertUnits.ToSimUnits(-45.0f) - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); + Collider.SimPosition.Y + 0.7f - colliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y - 0.7f); //if (movement.Y < 0) footPos.Y += 0.05f; diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index a41292c3b..af2737aad 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -71,7 +71,7 @@ namespace Barotrauma public bool onGround; private bool ignorePlatforms; - private float colliderHeightFromFloor; + protected float colliderHeightFromFloor; protected Structure stairs; @@ -978,7 +978,7 @@ namespace Barotrauma Vector2 rayStart = Collider.SimPosition; Vector2 rayEnd = rayStart; - rayEnd.Y -= Collider.height * 0.5f + Collider.radius + ConvertUnits.ToSimUnits(55.0f); + rayEnd.Y -= Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor*1.2f; var lowestLimb = FindLowestLimb(); @@ -1037,7 +1037,7 @@ namespace Barotrauma if (Math.Abs(Collider.SimPosition.Y - targetY) > 0.01f && Collider.SimPosition.Y Date: Thu, 17 Nov 2016 19:16:30 +0200 Subject: [PATCH 38/57] Water slows down players based on the depth of the water relative to the bottom of the collider --- .../Characters/Animation/HumanoidAnimController.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index a2c04379f..4da8aa2e3 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -256,17 +256,15 @@ namespace Barotrauma Vector2 colliderPos = GetColliderBottom(); if (Math.Abs(TargetMovement.X) > 1.0f) { - int limbsInWater = 0; - foreach (Limb limb in Limbs) + float slowdownAmount = 0.0f; + if (currentHull != null) { - if (limb.inWater) limbsInWater++; + //full slowdown (1.0f) when water is up to the torso + surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface); + slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / torsoPosition, 0.0f, 1.0f); } - float slowdownFactor = (float)limbsInWater / (float)Limbs.Length; - - float maxSpeed = Math.Max(TargetMovement.Length() - slowdownFactor, 1.0f); - // if (character.SelectedCharacter!=null) maxSpeed = Math.Min(maxSpeed, 1.0f); - + float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f); TargetMovement = Vector2.Normalize(TargetMovement) * maxSpeed; } From a4f00310dce2d04308afd836eb97effa9a3adb30 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 17 Nov 2016 20:00:03 +0200 Subject: [PATCH 39/57] "Floor check" raycasts ignore platforms/stairs based on the bottom pos of the collider instead of the lowest limb, fixed IndoorsSteeringManager node diff checks --- .../Source/Characters/AI/IndoorsSteeringManager.cs | 11 ++++------- Subsurface/Source/Characters/Animation/Ragdoll.cs | 10 +++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs index d5c045ee8..883022f5f 100644 --- a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs @@ -97,7 +97,7 @@ namespace Barotrauma diff.Y = 0.0f; } - if (diff.Length() < 0.01) return -host.Steering; + if (diff.LengthSquared() < 0.001f) return -host.Steering; return Vector2.Normalize(diff) * speed; } @@ -147,10 +147,8 @@ namespace Barotrauma var collider = character.AnimController.Collider; - Vector2 colliderBottom = character.AnimController.GetColliderBottom(); - if (Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X) < collider.radius*2 && - currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y && - currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + collider.height + collider.radius*2) + if (Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X) < collider.radius * 2 && + Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < collider.height / 2 + collider.radius) { currentPath.SkipToNextNode(); } @@ -167,8 +165,7 @@ namespace Barotrauma diff.X = 0.0f; //at the same height as the waypoint - if (currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y && - currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + collider.height + collider.radius * 2) + if (Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < collider.height / 2 + collider.radius) { float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY; diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index af2737aad..2747c4fea 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -980,11 +980,7 @@ namespace Barotrauma Vector2 rayEnd = rayStart; rayEnd.Y -= Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor*1.2f; - var lowestLimb = FindLowestLimb(); - - //TODO: use something like this instead of lowest limb, whenever we get to that - //float footY = Collider.SimPosition.Y + Collider.height * 0.5f + Collider.radius + ConvertUnits.ToSimUnits(45.0f); - + Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom()); if (!inWater && levitatingCollider) { float closestFraction = 1.0f; @@ -995,11 +991,11 @@ namespace Barotrauma { case Physics.CollisionStairs: Structure structure = fixture.Body.UserData as Structure; - if (lowestLimb.Position.Y Date: Thu, 17 Nov 2016 15:35:15 -0300 Subject: [PATCH 40/57] Fixed falling damage --- .../Source/Characters/Animation/HumanoidAnimController.cs | 1 + Subsurface/Source/Characters/Animation/Ragdoll.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index 4da8aa2e3..b7c86fecc 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -61,6 +61,7 @@ namespace Barotrauma if (character.IsDead || character.IsUnconscious || stunTimer > 0.0f) { + levitatingCollider = false; Collider.FarseerBody.FixedRotation = false; /*if (character.IsRemotePlayer) diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 2747c4fea..6cecf6b07 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -981,7 +981,7 @@ namespace Barotrauma rayEnd.Y -= Collider.height * 0.5f + Collider.radius + colliderHeightFromFloor*1.2f; Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom()); - if (!inWater && levitatingCollider) + if (!inWater && !character.IsDead && !character.IsUnconscious && levitatingCollider && Collider.LinearVelocity.Y>-ImpactTolerance) { float closestFraction = 1.0f; Fixture closestFixture = null; From 8c90fd17748b40df1f69d423e643dd52a8acbdd3 Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 18 Nov 2016 18:47:58 +0200 Subject: [PATCH 41/57] Doors push colliders away when closing, small human standing anim tweak --- Subsurface/Content/Characters/Human/human.xml | 2 +- .../Animation/HumanoidAnimController.cs | 21 +++++++----- .../Source/Characters/Animation/Ragdoll.cs | 3 ++ Subsurface/Source/Items/Components/Door.cs | 33 +++++++++++-------- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml index fb1644409..28b9914fe 100644 --- a/Subsurface/Content/Characters/Human/human.xml +++ b/Subsurface/Content/Characters/Human/human.xml @@ -3,7 +3,7 @@ - 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y); } - //ClimbOverObstacles(); - getUpSpeed = getUpSpeed * Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f); torso.pullJoint.Enabled = true; head.pullJoint.Enabled = true; waist.pullJoint.Enabled = true; - Collider.FarseerBody.Friction = 0.05f; - Collider.FarseerBody.Restitution = 0.05f; - if (stairs != null) { torso.pullJoint.WorldAnchorB = new Vector2( @@ -447,9 +442,19 @@ namespace Barotrauma for (int i = -1; i < 2; i += 2) { - Vector2 footPos = new Vector2( - Crouching ? waist.SimPosition.X + Math.Sign(stepSize.X * i) * Dir * 0.3f : GetCenterOfMass().X, - colliderPos.Y - 0.1f); + Vector2 footPos = colliderPos; + + if (Crouching) + { + footPos = new Vector2( + waist.SimPosition.X + Math.Sign(stepSize.X * i) * Dir * 0.3f, + colliderPos.Y - 0.1f); + } + else + { + footPos = new Vector2(GetCenterOfMass().X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f); + } + var foot = i == -1 ? rightFoot : leftFoot; diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 2747c4fea..a8e814a75 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -328,6 +328,9 @@ namespace Barotrauma break; case "collider": collider.Add(new PhysicsBody(subElement, scale)); + + collider[collider.Count - 1].FarseerBody.Friction = 0.05f; + collider[collider.Count - 1].FarseerBody.Restitution = 0.05f; collider[collider.Count - 1].FarseerBody.FixedRotation = true; collider[collider.Count - 1].CollisionCategories = Physics.CollisionCharacter; collider[collider.Count - 1].FarseerBody.AngularDamping = 5.0f; diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index 65a6096b2..ec03b232e 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Xml.Linq; using FarseerPhysics; using FarseerPhysics.Dynamics; @@ -7,6 +8,7 @@ using FarseerPhysics.Factories; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Barotrauma.Lights; +using System.Collections.Generic; namespace Barotrauma.Items.Components { @@ -409,50 +411,53 @@ namespace Barotrauma.Items.Components { int dir = isHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X); - foreach (Limb l in c.AnimController.Limbs) + List bodies = c.AnimController.Limbs.Select(l => l.body).ToList(); + bodies.Add(c.AnimController.Collider); + + foreach (PhysicsBody body in bodies) { float diff = 0.0f; if (isHorizontal) { - if (l.SimPosition.X < simPos.X || l.SimPosition.X > simPos.X + simSize.X) continue; + if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue; - diff = l.SimPosition.Y - item.SimPosition.Y; + diff = body.SimPosition.Y - item.SimPosition.Y; } else { - if (l.SimPosition.Y > simPos.Y || l.SimPosition.Y < simPos.Y - simSize.Y) continue; + if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y) continue; - diff = l.SimPosition.X - item.SimPosition.X; + diff = body.SimPosition.X - item.SimPosition.X; } if (Math.Sign(diff) != dir) { - SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, 1.0f, l.body); + SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, 1.0f, body); if (isHorizontal) { - l.body.SetTransform(new Vector2(l.SimPosition.X, item.SimPosition.Y + dir * simSize.Y * 2.0f), l.body.Rotation); - l.body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f)); + body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * simSize.Y * 2.0f), body.Rotation); + body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f)); } else { - l.body.SetTransform(new Vector2(item.SimPosition.X + dir * simSize.X * 1.2f, l.SimPosition.Y), l.body.Rotation); - l.body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f)); + body.SetTransform(new Vector2(item.SimPosition.X + dir * simSize.X * 1.2f, body.SimPosition.Y), body.Rotation); + body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f)); } } if (isHorizontal) { - if (Math.Abs(l.SimPosition.Y - item.SimPosition.Y) > simSize.Y * 0.5f) continue; + if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > simSize.Y * 0.5f) continue; - l.body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 0.5f)); + body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 0.5f)); } else { - if (Math.Abs(l.SimPosition.X - item.SimPosition.X) > simSize.X * 0.5f) continue; + if (Math.Abs(body.SimPosition.X - item.SimPosition.X) > simSize.X * 0.5f) continue; - l.body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f)); + body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f)); } c.StartStun(0.2f); From 2d6196b5f04cb20be9d53b1a2ae9a7cd10736e15 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Sat, 19 Nov 2016 22:15:58 -0300 Subject: [PATCH 42/57] Trim spaces from name Some troll exploited this on Landon's public server, so here's a fix. --- Subsurface/Source/Networking/Client.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 9cafb37d6..8ddece05c 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -90,6 +90,7 @@ namespace Barotrauma.Networking public static string SanitizeName(string name) { + name = name.Trim(); if (name.Length > 20) { name = name.Substring(0, 20); From 7a3bce39735c9a297f530210d4711408396b7035 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Sun, 20 Nov 2016 18:47:22 -0300 Subject: [PATCH 43/57] Hull volume helper in editor + Character name matches client name + Better-looking Watcher light source Also '' is a thing of the past, use \" instead --- Subsurface/Barotrauma.csproj | 1 - .../Content/Characters/Watcher/watcher.xml | 2 +- .../Source/Characters/Animation/Ragdoll.cs | 13 +++++- .../Source/Items/Components/ItemComponent.cs | 2 +- Subsurface/Source/Map/Lights/LightSource.cs | 7 ++- Subsurface/Source/Networking/ChatMessage.cs | 46 +++++++++---------- Subsurface/Source/Networking/GameClient.cs | 2 +- Subsurface/Source/Networking/GameServer.cs | 9 ++-- .../Source/Networking/GameServerLogin.cs | 10 ++-- Subsurface/Source/Screens/EditMapScreen.cs | 44 ++++++++++++++++++ Subsurface/Source/Sounds/SoundPlayer.cs | 2 +- Subsurface/Source/Utils/ToolBox.cs | 2 +- 12 files changed, 98 insertions(+), 42 deletions(-) diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index a38c3cead..9a72dc906 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -1485,7 +1485,6 @@ PreserveNewest - diff --git a/Subsurface/Content/Characters/Watcher/watcher.xml b/Subsurface/Content/Characters/Watcher/watcher.xml index c83554741..9cf6a49a4 100644 --- a/Subsurface/Content/Characters/Watcher/watcher.xml +++ b/Subsurface/Content/Characters/Watcher/watcher.xml @@ -14,7 +14,7 @@ - + diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index d1bcf0596..ad996634c 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -343,7 +343,7 @@ namespace Barotrauma if (collider[0] == null) { - DebugConsole.ThrowError("No collider configured for ''"+character.Name+"''!"); + DebugConsole.ThrowError("No collider configured for \""+character.Name+"\"!"); collider[0] = new PhysicsBody(0.0f, 0.0f, 0.5f, 5.0f); collider[0].BodyType = BodyType.Dynamic; collider[0].CollisionCategories = Physics.CollisionCharacter; @@ -624,7 +624,7 @@ namespace Barotrauma public virtual void Flip() { dir = (dir == Direction.Left) ? Direction.Right : Direction.Left; - + for (int i = 0; i < limbJoints.Length; i++) { float lowerLimit = -limbJoints[i].UpperLimit; @@ -648,6 +648,11 @@ namespace Barotrauma Limbs[i].Dir = Dir; + if (Limbs[i].LightSource != null) + { + Limbs[i].LightSource.SpriteEffect = (dir == Direction.Left) ? SpriteEffects.FlipHorizontally : SpriteEffects.None; + } + if (Limbs[i].pullJoint == null) continue; Limbs[i].pullJoint.LocalAnchorA = @@ -913,6 +918,10 @@ namespace Barotrauma } } + if (limb.LightSource != null) + { + limb.LightSource.Rotation = limb.Rotation; + } limb.Update(deltaTime); } diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index 82ade6dd9..bdd3a3735 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -119,7 +119,7 @@ namespace Barotrauma.Items.Components if (value == drawable) return; if (!(this is IDrawableComponent)) { - DebugConsole.ThrowError("Couldn't make ''"+this+"'' drawable (the component doesn't implement the IDrawableComponent interface)"); + DebugConsole.ThrowError("Couldn't make \""+this+"\" drawable (the component doesn't implement the IDrawableComponent interface)"); return; } diff --git a/Subsurface/Source/Map/Lights/LightSource.cs b/Subsurface/Source/Map/Lights/LightSource.cs index 36f7dbd3b..770d25443 100644 --- a/Subsurface/Source/Map/Lights/LightSource.cs +++ b/Subsurface/Source/Map/Lights/LightSource.cs @@ -18,6 +18,8 @@ namespace Barotrauma.Lights private float range; + public SpriteEffects SpriteEffect = SpriteEffects.None; + private Texture2D texture; public Sprite LightSprite; @@ -312,13 +314,14 @@ namespace Barotrauma.Lights overrideLightTexture.Draw(spriteBatch, drawPos, color * (color.A / 255.0f), overrideLightTexture.Origin, -Rotation, - new Vector2(overrideLightTexture.size.X / overrideLightTexture.SourceRect.Width, overrideLightTexture.size.Y / overrideLightTexture.SourceRect.Height)); + new Vector2(overrideLightTexture.size.X / overrideLightTexture.SourceRect.Width, overrideLightTexture.size.Y / overrideLightTexture.SourceRect.Height), + SpriteEffect); } } if (LightSprite != null) { - LightSprite.Draw(spriteBatch, drawPos, Color, LightSprite.Origin); + LightSprite.Draw(spriteBatch, drawPos, Color, LightSprite.Origin, -Rotation, 1, SpriteEffect); } } diff --git a/Subsurface/Source/Networking/ChatMessage.cs b/Subsurface/Source/Networking/ChatMessage.cs index ed71aa84c..4386318c0 100644 --- a/Subsurface/Source/Networking/ChatMessage.cs +++ b/Subsurface/Source/Networking/ChatMessage.cs @@ -116,9 +116,9 @@ namespace Barotrauma.Networking public void WriteNetworkMessage(NetOutgoingMessage msg) { - msg.WriteRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length, (byte)Type); - if (GameMain.Server != null) - { + msg.WriteRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length, (byte)Type); + if (GameMain.Server != null) + { msg.Write(Sender == null ? (ushort)0 : Sender.ID); msg.Write(SenderName); } @@ -128,30 +128,30 @@ namespace Barotrauma.Networking public static ChatMessage ReadNetworkMessage(NetBuffer msg) { - ChatMessageType type = (ChatMessageType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length); - string senderName=""; - Character character = null; - if (GameMain.Server == null) - { + ChatMessageType type = (ChatMessageType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ChatMessageType)).Length); + string senderName=""; + Character character = null; + if (GameMain.Server == null) + { ushort senderId = msg.ReadUInt16(); character = Entity.FindEntityByID(senderId) as Character; senderName = msg.ReadString(); } - else - { - NetIncomingMessage inc = msg as NetIncomingMessage; - if (inc == null) return null; - Client sender = GameMain.Server.ConnectedClients.Find(x => x.Connection == inc.SenderConnection); - if (sender == null) return null; - character = sender.Character; - if (character != null) - { - senderName = character.Name; - } - else - { - senderName = sender.name; - } + else + { + NetIncomingMessage inc = msg as NetIncomingMessage; + if (inc == null) return null; + Client sender = GameMain.Server.ConnectedClients.Find(x => x.Connection == inc.SenderConnection); + if (sender == null) return null; + character = sender.Character; + if (character != null) + { + senderName = character.Name; + } + else + { + senderName = sender.name; + } } string text = msg.ReadString(); diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 0c92a62f6..10d3a03ed 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -1117,7 +1117,7 @@ namespace Barotrauma.Networking NetOutgoingMessage msg = client.CreateMessage(); msg.Write((byte)PacketTypes.CharacterInfo); - msg.Write(characterInfo.Name); + //msg.Write(characterInfo.Name); msg.Write(characterInfo.Gender == Gender.Male); msg.Write((byte)characterInfo.HeadSpriteId); diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 82fda3bbf..19907eeb1 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -1719,24 +1719,25 @@ namespace Barotrauma.Networking Gender gender = Gender.Male; int headSpriteId = 0; + name = sender.name; try { - name = message.ReadString(); + //name = message.ReadString(); gender = message.ReadBoolean() ? Gender.Male : Gender.Female; headSpriteId = message.ReadByte(); } catch { - name = ""; + //name = ""; gender = Gender.Male; headSpriteId = 0; } - if (sender.characterInfo != null) + /*if (sender.characterInfo != null) { //clients can't change their character's name once it's been set name = sender.characterInfo.Name; - } + }*/ List jobPreferences = new List(); int count = message.ReadByte(); diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index 5008d08b5..adf3d3de5 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -143,14 +143,14 @@ namespace Barotrauma.Networking inc.SenderConnection.Disconnect("The name \"" + name + "\" is already in use. Please choose another name."); DebugConsole.NewMessage(name + " couldn't join the server (name already in use)", Color.Red); return; - } - + } + #endif - if (!whitelist.IsWhiteListed(name, inc.SenderConnection.RemoteEndPoint.Address.ToString())) - { + if (!whitelist.IsWhiteListed(name, inc.SenderConnection.RemoteEndPoint.Address.ToString())) + { inc.SenderConnection.Disconnect("You're not in this server's whitelist."); DebugConsole.NewMessage(name + " (" + inc.SenderConnection.RemoteEndPoint.Address.ToString() + ") couldn't join the server (not in whitelist)", Color.Red); - return; + return; } //existing user re-joining diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index 06f926915..e798b90bc 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -63,6 +63,41 @@ namespace Barotrauma return "Structures: " + (MapEntity.mapEntityList.Count - Item.ItemList.Count); } + private string GetTotalHullVolume() + { + float totalVol = 0.0f; + Hull.hullList.ForEach(h => { totalVol += h.FullVolume; }); + return "Total Hull Volume:\n" + totalVol; + } + + private string GetSelectedHullVolume() + { + float buoyancyVol = 0.0f; + float selectedVol = 0.0f; + float neutralPercentage = 0.07f; + Hull.hullList.ForEach(h => { + buoyancyVol += h.FullVolume; + if (h.IsSelected) + { + selectedVol += h.FullVolume; + } + }); + buoyancyVol *= neutralPercentage; + string retVal = "Selected Hull Volume:\n" + selectedVol; + if (selectedVol>0.0f && buoyancyVol>0.0f) + { + if (buoyancyVol / selectedVol < 1.0f) + { + retVal += " (optimal NeutralBallastLevel is " + (buoyancyVol / selectedVol) + ")"; + } + else + { + retVal += " (insufficient volume for buoyancy control)"; + } + } + return retVal; + } + private string GetPhysicsBodyCount() { return "Physics bodies: " + GameMain.World.BodyList.Count; @@ -89,6 +124,15 @@ namespace Barotrauma topPanel = new GUIFrame(new Rectangle(0, 0, 0, 31), GUI.Style); topPanel.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + GUIFrame hullVolumeFrame = new GUIFrame(new Rectangle(145, 26, 400, 100), GUI.Style, topPanel); + hullVolumeFrame.Padding = new Vector4(3.0f, 3.0f, 3.0f, 3.0f); + + GUITextBlock totalHullVolume = new GUITextBlock(new Rectangle(0, 0, 0, 20), "", GUI.Style, hullVolumeFrame); + totalHullVolume.TextGetter = GetTotalHullVolume; + + GUITextBlock selectedHullVolume = new GUITextBlock(new Rectangle(0, 50, 0, 20), "", GUI.Style, hullVolumeFrame); + selectedHullVolume.TextGetter = GetSelectedHullVolume; + var button = new GUIButton(new Rectangle(0, 0, 70, 20), "Open...", GUI.Style, topPanel); button.OnClicked = CreateLoadScreen; diff --git a/Subsurface/Source/Sounds/SoundPlayer.cs b/Subsurface/Source/Sounds/SoundPlayer.cs index 4fbe35629..9053d899e 100644 --- a/Subsurface/Source/Sounds/SoundPlayer.cs +++ b/Subsurface/Source/Sounds/SoundPlayer.cs @@ -314,7 +314,7 @@ namespace Barotrauma { return musicClips.Where(x => x != null && x.type == OverrideMusicType).ToList(); } - else if (Character.Controlled != null && Level.Loaded != null && Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition))) + else if (Character.Controlled != null && Level.Loaded != null && Level.Loaded.Ruins!=null && Level.Loaded.Ruins.Any(r => r.Area.Contains(Character.Controlled.WorldPosition))) { return musicClips.Where(x => x != null && x.type == "ruins").ToList(); } diff --git a/Subsurface/Source/Utils/ToolBox.cs b/Subsurface/Source/Utils/ToolBox.cs index 101f2dd00..decd88e39 100644 --- a/Subsurface/Source/Utils/ToolBox.cs +++ b/Subsurface/Source/Utils/ToolBox.cs @@ -320,7 +320,7 @@ namespace Barotrauma if (components.Length!=3) { if (!errorMessages) return vector; - DebugConsole.ThrowError("Failed to parse the string ''"+stringVector3+"'' to Vector3"); + DebugConsole.ThrowError("Failed to parse the string \""+stringVector3+"\" to Vector3"); return vector; } From ba7fcad8f2d3edd772b46d0a5ed74d42c6d5e87c Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Sun, 20 Nov 2016 22:01:37 -0300 Subject: [PATCH 44/57] Readded special client permissions Also added a command that requires a password to gain the permissions. I think they were never vulnerable in the first place, gamerfood was just trying to intimidate us. --- Subsurface/Source/DebugConsole.cs | 20 +++++++++++-- Subsurface/Source/Networking/Client.cs | 6 ++-- Subsurface/Source/Networking/GameClient.cs | 15 ++++++++-- Subsurface/Source/Networking/GameServer.cs | 29 ++++++++++++++++++- .../Source/Networking/GameServerLogin.cs | 4 +++ .../Source/Networking/GameServerSettings.cs | 14 +++++++++ Subsurface/Source/Networking/NetworkMember.cs | 2 ++ 7 files changed, 81 insertions(+), 9 deletions(-) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index d18889d4e..2e3947bc7 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -6,6 +6,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Barotrauma.Networking; using Barotrauma.Items.Components; +using System.Text; namespace Barotrauma { @@ -123,7 +124,6 @@ namespace Barotrauma if (PlayerInput.KeyDown(Keys.Enter) && textBox.Text != "") { - NewMessage(textBox.Text, Color.White); ExecuteCommand(textBox.Text, game); textBox.Text = ""; @@ -165,6 +165,7 @@ namespace Barotrauma case "netstats": case "help": case "dumpids": + case "admin": return true; default: return false; @@ -175,7 +176,11 @@ namespace Barotrauma { if (string.IsNullOrWhiteSpace(command)) return; string[] commands = command.Split(' '); - + + if (!commands[0].ToLowerInvariant().Equals("admin")) + { + NewMessage(textBox.Text, Color.White); + } #if !DEBUG if (GameMain.Client != null && !IsCommandPermitted(commands[0].ToLowerInvariant(), GameMain.Client)) @@ -370,6 +375,17 @@ namespace Barotrauma case "enablecrewai": HumanAIController.DisableCrewAI = false; break; + case "admin": + if (GameMain.Server != null) + { + GameMain.Server.AdminAuthPass = commands[1]; + + } + else if (GameMain.Client != null) + { + GameMain.Client.RequestAdminAuth(commands[1]); + } + break; case "kick": if (GameMain.NetworkMember == null || commands.Length < 2) break; GameMain.NetworkMember.KickPlayer(string.Join(" ", commands.Skip(1)), false); diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 8ddece05c..5d3e6ee96 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -52,13 +52,13 @@ namespace Barotrauma.Networking public float deleteDisconnectedTimer; - public ClientPermissions Permissions; + public ClientPermissions Permissions = ClientPermissions.None; public int KickVoteCount { get { return kickVoters.Count; } } - + public Client(NetPeer server, string name, byte ID) : this(name, ID) { @@ -129,7 +129,7 @@ namespace Barotrauma.Networking public bool HasPermission(ClientPermissions permission) { - return false; //Permissions.HasFlag(permission); + return this.Permissions.HasFlag(permission); } public T GetVote(VoteType voteType) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 10d3a03ed..887b7854b 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -26,7 +26,7 @@ namespace Barotrauma.Networking private GUIButton endRoundButton; private GUITickBox endVoteTickBox; - private ClientPermissions permissions; + private ClientPermissions permissions = ClientPermissions.None; private bool connected; @@ -839,7 +839,7 @@ namespace Barotrauma.Networking public bool HasPermission(ClientPermissions permission) { - return false;// permissions.HasFlag(permission); + return permissions.HasFlag(permission); } public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) @@ -1026,6 +1026,15 @@ namespace Barotrauma.Networking client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); } + public void RequestAdminAuth(string pass) + { + NetOutgoingMessage msg = client.CreateMessage(); + msg.Write((byte)PacketTypes.RequestAdminAuth); + msg.Write(Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(pass)))); + + client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + } + public override void KickPlayer(string kickedName, bool ban, bool range = false) { if (!permissions.HasFlag(ClientPermissions.Kick) && !ban) return; @@ -1036,7 +1045,7 @@ namespace Barotrauma.Networking msg.Write(ban); msg.Write(kickedName); - client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); } public bool VoteForKick(GUIButton button, object userdata) diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 19907eeb1..0ec6463da 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -55,6 +55,8 @@ namespace Barotrauma.Networking name = name.Replace(":", ""); name = name.Replace(";", ""); + AdminAuthPass = ""; + this.name = name; this.password = ""; if (password.Length>0) @@ -551,6 +553,32 @@ namespace Barotrauma.Networking EndGame(); } break; + case (byte)PacketTypes.RequestAdminAuth: + string pass = inc.ReadString(); + if (adminAuthPass.Length == 0) + { + Log(sender.name + " tried to become admin!", Color.Red); + return; + } + if (adminAuthPass==pass) + { + if (sender.Permissions == ClientPermissions.None) + { + Log(sender.name + " is now an admin.", Color.Yellow); + sender.SetPermissions(ClientPermissions.Kick | ClientPermissions.Ban | ClientPermissions.EndRound); + } + else + { + Log(sender.name + " is no longer an admin.", Color.Yellow); + sender.SetPermissions(ClientPermissions.None); + } + UpdateClientPermissions(sender); + } + else + { + Log(sender.name + " has failed admin authentication!", Color.Red); + } + break; case (byte)PacketTypes.KickPlayer: bool ban = inc.ReadBoolean(); string kickedName = inc.ReadString(); @@ -561,7 +589,6 @@ namespace Barotrauma.Networking if (ban && !sender.HasPermission(ClientPermissions.Ban)) { Log(sender.name + " attempted to ban " + kickedClient.name + " (insufficient permissions)", Color.Red); - } else if (!sender.HasPermission(ClientPermissions.Kick)) { diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index adf3d3de5..66ab12f7c 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -198,6 +198,10 @@ namespace Barotrauma.Networking { newClient.SetPermissions(savedPermissions.Permissions); } + else + { + newClient.SetPermissions(ClientPermissions.None); + } connectedClients.Add(newClient); diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index 41432ee04..b89738ede 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -63,6 +63,20 @@ namespace Barotrauma.Networking private string password; + private string adminAuthPass = ""; + public string AdminAuthPass + { + set + { + DebugConsole.NewMessage("Admin auth pass changed!",Color.Yellow); + adminAuthPass = ""; + if (value.Length > 0) + { + adminAuthPass = Encoding.UTF8.GetString(Lidgren.Network.NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(value))); + } + } + } + private GUIFrame settingsFrame; private GUIFrame[] settingsTabs; private int settingsTabIndex; diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 21edefb26..8ff348815 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -19,6 +19,8 @@ namespace Barotrauma.Networking KickPlayer, + RequestAdminAuth, + Permissions, RequestNetLobbyUpdate, From 145abe03f182379ad2f85f90f2d19635c3cc2898 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Sun, 20 Nov 2016 22:04:21 -0300 Subject: [PATCH 45/57] Fixed crash with admin command --- Subsurface/Source/DebugConsole.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 2e3947bc7..a505c9da7 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -376,6 +376,8 @@ namespace Barotrauma HumanAIController.DisableCrewAI = false; break; case "admin": + if (commands.Length < 2) break; + if (GameMain.Server != null) { GameMain.Server.AdminAuthPass = commands[1]; From 50d706c67b6f9f2231afd3b4030704b686da025e Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 19 Nov 2016 14:53:53 +0200 Subject: [PATCH 46/57] - changed how PowerContainers determine how much power to provide to the grid, batteries can now match the load of the grid - light-emitting alien structures that hold the artifacts instead of just having the artifacts lay on the floor (can also be used for turning artifacts into power sources if installed in a sub) --- Subsurface/Barotrauma.csproj | 3 ++ .../Items/Artifacts/artifactholder.png | Bin 0 -> 98849 bytes .../Content/Items/Artifacts/artifacts.xml | 43 +++++++++++++++-- Subsurface/Content/Map/RuinConfig.xml | 2 + Subsurface/Source/Events/ArtifactEvent.cs | 16 +++++++ .../Items/Components/Power/PowerContainer.cs | 45 ++++++++---------- 6 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 Subsurface/Content/Items/Artifacts/artifactholder.png diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index a38c3cead..662e956f3 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -524,6 +524,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest Designer diff --git a/Subsurface/Content/Items/Artifacts/artifactholder.png b/Subsurface/Content/Items/Artifacts/artifactholder.png new file mode 100644 index 0000000000000000000000000000000000000000..bca7f24fe3248110d87b655f62919ff0e9809560 GIT binary patch literal 98849 zcmV*4Ky|-~P)(_`g8%^e{{R4h=l}px2mk>USO5SzmjD14Z`WEM zkN^RJr%6OXRCwC#{dv%>YkJm){hsL^*Sx-Q&wHQKXXx(J-Rf3L-9l;!gc_lQ0wuyW z1w|mnMX(uSz;TG<6t*EY#5PHsp=>L5jAI<5B0!}aE9xjr5^7rNK|P%AGwieH{mtJR z-|?C9$JbCEpu!}(TfMKpR;^lV)vEh_-s`=e=f3Xi7BNO%;03-_@!{7#GVWyZS4PuW zthQCAg&n3Q5p^G>>+HR6eB-m-izxlWL%bk>7x-4ihhF=Emxb>BAFf}%;=K0gB-t62 zIs{21&|3b$#k%<~Vpo&oS-swFVxkNoh9=7f5vg!}&C{n}Tfd0r3j%n7Z!^4d>!a@k z<6bT@$KuRU3=N0#3Cd&yX{atA*B6zxDW0IyP<3q-Qj!Tmk47SCn(fn~9K5Hc`?S(JCWj>{DI=ZUGB$8rSvb}s7c6aAL`HNrv!tZ?%-4_J#0^h>8cW_6~uiyH{!zT}4 zDwN>HjT`gq+eaqtCY4^MDM{i7^6`VI@^*YMG&U^ zohL_M`%hj(`3m|)2wvb@4Mmy$=-I`|OKFzS*IOPPKc?+02ct2wK~CS*WNL<2L*9P- zgf?D~B|W0wkfbTd5g|7C*rQP_8jI2y!66gre;jy0051sOR}N4AwSQWaZ`{O;;l0le z`OqKxBQMgde*jo(PkR@b495hmae%_1bxM{bcvo|Av7&7SMcmN)h!+J?XnauQMZsph zAuYyiI!8#0t8}-wm_*)t5#bjE@GFMj^+P}KC+;NT1Nj%;VKPp+f2^MU=|A>Ie*cTe z{QaYK;tN$PgVc>Iq@WKGEhONvzC&usc$m;dIG7sl-F}U_f&SY7@1MEIjuNnpaOO8sJ+HS&2W&sCv2(0)iw3$1J0j4VldjpT-K;eUwh@&2mYF?+oqEK)tyu++xO`4?48P~AA9uP7rtZE zZNKK@pZ=ZI;{1=ct{X*}ZOV|mGfjry-Dx||&M)ZFgvIihX}cm6AHe7oWk8#Vz;jXe z9DVNR5XCKm@5p4rKN|YHxL8DSxal{4~l7yAi``$Dh#h*dlkBd$!LV@8}c-V0LEAbqsUp)F*A-uw}9yl zTpLizk$Q`Bj&V^UTx7-MpvW}q=9s2k{;9?BTi-D! zv+I84QIafn=wV751nDL+HXf52Hb)Pcj0&piDQTLJ<~hUBfMJm_$qoDS>x_(oEWwQg zneh-h5Q2JnLZ2&o@4)HgKm1Go&%f|u^}fge{*iHfvHkPXbswgvvL=YvBAL$K{szU+ z!{O^x^_DzIkX=Jrkp+0a@HK2_r*;dfo8iUh$Tu-X`N-q=0CE3aO>Bf96$d5yjaCA2;d(Y%kzi7=Wo9*W`~cDFXGMT z0S23fAGtRI(bBdpo7PZ{W)#Pt!;Z%IY>I3YB$-B=Rw%q_J*T_7@+S}Ms zhiN@7Pw*~4Q!$QEuWFgh27d;44SHy@njOTfo6}R92@<%r$$>rt6<)hBF|54X;|3TfHeCr-6 zw)Lyw;+(b|(ls4AdZwKwH6<>14(7LsKoL{4Z%{f#N

VpjE=q_GI%3&lel=B%`gi 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 literal 0 HcmV?d00001 diff --git a/Subsurface/Content/Items/Artifacts/artifacts.xml b/Subsurface/Content/Items/Artifacts/artifacts.xml index 3f11229ed..3a6b3ee62 100644 --- a/Subsurface/Content/Items/Artifacts/artifacts.xml +++ b/Subsurface/Content/Items/Artifacts/artifacts.xml @@ -3,6 +3,7 @@ @@ -24,6 +25,7 @@ @@ -46,6 +48,7 @@ @@ -66,7 +69,7 @@ name="Oxygenite Shard" category="Alien" pickdistance="150" - tags="smallitem" + tags="alien,smallitem" impacttolerance="8"> @@ -86,7 +89,7 @@ name="Sulphurite Shard" category="Alien" pickdistance="150" - tags="smallitem" + tags="alien,smallitem" impacttolerance="8" spritecolor="1.0,0.0,0.0,1.0"> @@ -109,7 +112,7 @@ @@ -147,6 +150,7 @@ @@ -169,6 +173,7 @@ name="Alien Door" category="Alien" linkable="true" + Tags="alien" pickdistance="150.0"> @@ -190,6 +195,7 @@ name="Alien Motion Sensor" linkable="true" category="Alien" + Tags="alien" pickdistance="150.0"> @@ -201,4 +207,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Map/RuinConfig.xml b/Subsurface/Content/Map/RuinConfig.xml index f843d1829..6953c9539 100644 --- a/Subsurface/Content/Map/RuinConfig.xml +++ b/Subsurface/Content/Map/RuinConfig.xml @@ -27,4 +27,6 @@ + + \ No newline at end of file diff --git a/Subsurface/Source/Events/ArtifactEvent.cs b/Subsurface/Source/Events/ArtifactEvent.cs index 70767c0c5..3cbc2b822 100644 --- a/Subsurface/Source/Events/ArtifactEvent.cs +++ b/Subsurface/Source/Events/ArtifactEvent.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using System; using System.Xml.Linq; namespace Barotrauma @@ -37,6 +38,21 @@ namespace Barotrauma item = new Item(itemPrefab, position, null); item.MoveWithLevel = true; item.body.FarseerBody.IsKinematic = true; + + //try to find a nearby artifact holder (or any alien itemcontainer) and place the artifact inside it + foreach (Item it in Item.ItemList) + { + if (it.Submarine != null || !it.HasTag("alien")) continue; + + if (Math.Abs(item.WorldPosition.X - it.WorldPosition.X) > 2000.0f) continue; + if (Math.Abs(item.WorldPosition.Y - it.WorldPosition.Y) > 2000.0f) continue; + + var itemContainer = it.GetComponent(); + if (itemContainer == null) continue; + + itemContainer.Combine(item); + break; + } } public override void Update(float deltaTime) diff --git a/Subsurface/Source/Items/Components/Power/PowerContainer.cs b/Subsurface/Source/Items/Components/Power/PowerContainer.cs index f793dce9d..b5377d5b8 100644 --- a/Subsurface/Source/Items/Components/Power/PowerContainer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerContainer.cs @@ -82,23 +82,26 @@ namespace Barotrauma.Items.Components IsActive = true; - var button = new GUIButton(new Rectangle(160, 50, 30,30), "-", GUI.Style, GuiFrame); - button.OnClicked = (GUIButton btn, object obj) => + if (canBeSelected) { - RechargeSpeed = Math.Max(rechargeSpeed - maxRechargeSpeed * 0.1f, 0.0f); - item.NewComponentEvent(this, true, false); + var button = new GUIButton(new Rectangle(160, 50, 30,30), "-", GUI.Style, GuiFrame); + button.OnClicked = (GUIButton btn, object obj) => + { + RechargeSpeed = Math.Max(rechargeSpeed - maxRechargeSpeed * 0.1f, 0.0f); + item.NewComponentEvent(this, true, false); - return true; - }; + return true; + }; - button = new GUIButton(new Rectangle(200, 50, 30, 30), "+", GUI.Style, GuiFrame); - button.OnClicked = (GUIButton btn, object obj) => - { - RechargeSpeed = Math.Max(rechargeSpeed + maxRechargeSpeed * 0.1f, 0.0f); - item.NewComponentEvent(this, true, false); + button = new GUIButton(new Rectangle(200, 50, 30, 30), "+", GUI.Style, GuiFrame); + button.OnClicked = (GUIButton btn, object obj) => + { + RechargeSpeed = Math.Max(rechargeSpeed + maxRechargeSpeed * 0.1f, 0.0f); + item.NewComponentEvent(this, true, false); - return true; - }; + return true; + }; + } } public override bool Pick(Character picker) @@ -167,28 +170,18 @@ namespace Barotrauma.Items.Components return; } - //currPowerConsumption = MathHelper.Lerp( - // currPowerConsumption, - // -maxOutput * chargeRate, - // 0.1f); - if (gridPower < gridLoad) { - // CurrPowerOutput = MathHelper.Lerp( - //CurrPowerOutput, Math.Min(maxOutput * chargeRate, gridLoad), 0.05f); - CurrPowerOutput = MathHelper.Lerp( CurrPowerOutput, - Math.Min(maxOutput * chargeRate, gridLoad - (gridLoad * outputVoltage)), - 0.05f); + Math.Min(maxOutput * chargeRate, gridLoad), + deltaTime); } else { - CurrPowerOutput = MathHelper.Lerp(CurrPowerOutput, 0.0f, 0.05f); + CurrPowerOutput = MathHelper.Lerp(CurrPowerOutput, 0.0f, deltaTime); } - - //powerConsumption = Math.Min(powerConsumption, 0.0f); Charge -= CurrPowerOutput / 3600.0f; } From 4cef01113101d345a711bd4afad4fe0fdaaef2fb Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 21 Nov 2016 16:55:56 +0200 Subject: [PATCH 47/57] Hull volume helper is hidden when no hulls are selected, using the HasPermission method for consistency --- Subsurface/Source/Networking/GameClient.cs | 10 +++++----- Subsurface/Source/Screens/EditMapScreen.cs | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 887b7854b..9ea9ac820 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -256,7 +256,7 @@ namespace Barotrauma.Networking bool hasCharacter = inc.ReadBoolean(); bool allowSpectating = inc.ReadBoolean(); - endRoundButton.Visible = permissions.HasFlag(ClientPermissions.EndRound); + endRoundButton.Visible = HasPermission(ClientPermissions.EndRound); if (gameStarted && Screen.Selected != GameMain.GameScreen) { @@ -613,7 +613,7 @@ namespace Barotrauma.Networking msg = "Your current permissions:\n"; foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) { - if (!newPermissions.HasFlag(permissions) || permission == ClientPermissions.None) continue; + if (!HasPermission(permissions) || permission == ClientPermissions.None) continue; System.Reflection.FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); @@ -625,7 +625,7 @@ namespace Barotrauma.Networking new GUIMessageBox("Permissions changed", msg).UserData = "permissions"; } - endRoundButton.Visible = permissions.HasFlag(ClientPermissions.EndRound); + endRoundButton.Visible = HasPermission(ClientPermissions.EndRound); break; case (byte)PacketTypes.RequestFile: @@ -1037,8 +1037,8 @@ namespace Barotrauma.Networking public override void KickPlayer(string kickedName, bool ban, bool range = false) { - if (!permissions.HasFlag(ClientPermissions.Kick) && !ban) return; - if (!permissions.HasFlag(ClientPermissions.Ban) && ban) return; + if (!HasPermission(ClientPermissions.Kick) && !ban) return; + if (!HasPermission(ClientPermissions.Ban) && ban) return; NetOutgoingMessage msg = client.CreateMessage(); msg.Write((byte)PacketTypes.KickPlayer); diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index e798b90bc..bd26e75ec 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -25,6 +25,8 @@ namespace Barotrauma private GUITextBox nameBox; + private GUIFrame hullVolumeFrame; + const int PreviouslyUsedCount = 10; private GUIListBox previouslyUsedList; @@ -88,7 +90,7 @@ namespace Barotrauma { if (buoyancyVol / selectedVol < 1.0f) { - retVal += " (optimal NeutralBallastLevel is " + (buoyancyVol / selectedVol) + ")"; + retVal += " (optimal NeutralBallastLevel is " + (buoyancyVol / selectedVol).ToString("0.00") + ")"; } else { @@ -124,13 +126,14 @@ namespace Barotrauma topPanel = new GUIFrame(new Rectangle(0, 0, 0, 31), GUI.Style); topPanel.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); - GUIFrame hullVolumeFrame = new GUIFrame(new Rectangle(145, 26, 400, 100), GUI.Style, topPanel); + hullVolumeFrame = new GUIFrame(new Rectangle(145, 26, 280, 70), GUI.Style, topPanel); hullVolumeFrame.Padding = new Vector4(3.0f, 3.0f, 3.0f, 3.0f); - GUITextBlock totalHullVolume = new GUITextBlock(new Rectangle(0, 0, 0, 20), "", GUI.Style, hullVolumeFrame); + GUITextBlock totalHullVolume = new GUITextBlock(new Rectangle(0, 0, 0, 20), "", GUI.Style, hullVolumeFrame, GUI.SmallFont); + totalHullVolume.Visible = false; totalHullVolume.TextGetter = GetTotalHullVolume; - GUITextBlock selectedHullVolume = new GUITextBlock(new Rectangle(0, 50, 0, 20), "", GUI.Style, hullVolumeFrame); + GUITextBlock selectedHullVolume = new GUITextBlock(new Rectangle(0, 30, 0, 20), "", GUI.Style, hullVolumeFrame, GUI.SmallFont); selectedHullVolume.TextGetter = GetSelectedHullVolume; var button = new GUIButton(new Rectangle(0, 0, 70, 20), "Open...", GUI.Style, topPanel); @@ -894,6 +897,8 @@ namespace Barotrauma { if (tutorial != null) tutorial.Update((float)deltaTime); + hullVolumeFrame.Visible = MapEntity.SelectedList.Any(s => s is Hull); + if (GUIComponent.MouseOn == null) { cam.MoveCamera((float)deltaTime); From c3b84ca83545b54d77913dadd02017c492d08e96 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 21 Nov 2016 17:46:03 +0200 Subject: [PATCH 48/57] Re-enabled client permission settings in NetLobbyScreen, fixed clients not setting their permissions when receiving PacketTypes.Permissions --- Subsurface/Source/Networking/Client.cs | 4 +- Subsurface/Source/Networking/GameClient.cs | 3 +- Subsurface/Source/Screens/NetLobbyScreen.cs | 76 +++++++++++---------- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 5d3e6ee96..dd4173bb5 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -119,12 +119,12 @@ namespace Barotrauma.Networking public void GivePermission(ClientPermissions permission) { - this.Permissions |= permission; + if (!this.Permissions.HasFlag(permission)) this.Permissions |= permission; } public void RemovePermission(ClientPermissions permission) { - this.Permissions &= ~permission; + if (this.Permissions.HasFlag(permission)) this.Permissions &= ~permission; } public bool HasPermission(ClientPermissions permission) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 9ea9ac820..bc2617c79 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -613,7 +613,7 @@ namespace Barotrauma.Networking msg = "Your current permissions:\n"; foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) { - if (!HasPermission(permissions) || permission == ClientPermissions.None) continue; + if (!newPermissions.HasFlag(permission) || permission == ClientPermissions.None) continue; System.Reflection.FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); @@ -622,6 +622,7 @@ namespace Barotrauma.Networking } } + permissions = newPermissions; new GUIMessageBox("Permissions changed", msg).UserData = "permissions"; } diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index b0f91015c..fbc55c8f8 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -9,6 +9,8 @@ using FarseerPhysics.Dynamics; using System.IO; using System.Linq; using System.Collections.Generic; +using System.Reflection; +using System.ComponentModel; namespace Barotrauma { @@ -772,12 +774,12 @@ namespace Barotrauma } } - playerFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), Color.Black * 0.3f); + playerFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), Color.Black * 0.6f); - var playerFrameInner = new GUIFrame(new Rectangle(0, 0, 300, 250), null, Alignment.Center, GUI.Style, playerFrame); + var playerFrameInner = new GUIFrame(new Rectangle(0, 0, 300, 280), null, Alignment.Center, GUI.Style, playerFrame); playerFrameInner.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); - new GUITextBlock(new Rectangle(0,0,200,20), component.UserData.ToString(), + new GUITextBlock(new Rectangle(0, 0, 200, 20), component.UserData.ToString(), GUI.Style, Alignment.TopLeft, Alignment.TopLeft, playerFrameInner, false, GUI.LargeFont); @@ -787,50 +789,50 @@ namespace Barotrauma new GUITextBlock(new Rectangle(0, 25, 150, 15), selectedClient.Connection.RemoteEndPoint.Address.ToString(), GUI.Style, playerFrameInner); - //var permissionsBox = new GUIFrame(new Rectangle(0, 60, 0, 85), GUI.Style, playerFrameInner); - //permissionsBox.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); - //permissionsBox.UserData = selectedClient; + var permissionsBox = new GUIFrame(new Rectangle(0, 60, 0, 90), GUI.Style, playerFrameInner); + permissionsBox.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + permissionsBox.UserData = selectedClient; - //new GUITextBlock(new Rectangle(0, 0, 0, 15), "Permissions:", GUI.Style, permissionsBox); - //int x = 0, y = 0; - //foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) - //{ - // if (permission == ClientPermissions.None) continue; + new GUITextBlock(new Rectangle(0, 0, 0, 15), "Permissions:", GUI.Style, permissionsBox); + int x = 0, y = 0; + foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) + { + if (permission == ClientPermissions.None) continue; - // FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); - // DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); + DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); - // string permissionStr = attributes.Length > 0 ? attributes[0].Description : permission.ToString(); - - // var permissionTick = new GUITickBox(new Rectangle(x,y+20,15,15), permissionStr, Alignment.TopLeft, GUI.SmallFont, permissionsBox); - // permissionTick.UserData = permission; - // permissionTick.Selected = selectedClient.HasPermission(permission); + string permissionStr = attributes.Length > 0 ? attributes[0].Description : permission.ToString(); - // permissionTick.OnSelected = (tickBox) => - // { - // var client = tickBox.Parent.UserData as Client; - // if (client == null) return false; + var permissionTick = new GUITickBox(new Rectangle(x, y + 20, 15, 15), permissionStr, Alignment.TopLeft, GUI.SmallFont, permissionsBox); + permissionTick.UserData = permission; + permissionTick.Selected = selectedClient.HasPermission(permission); - // var thisPermission = (ClientPermissions)tickBox.UserData; + permissionTick.OnSelected = (tickBox) => + { + var client = tickBox.Parent.UserData as Client; + if (client == null) return false; - // if (tickBox.Selected) - // client.GivePermission(thisPermission); - // else - // client.RemovePermission(thisPermission); + var thisPermission = (ClientPermissions)tickBox.UserData; - // GameMain.Server.UpdateClientPermissions(client); + if (tickBox.Selected) + client.GivePermission(thisPermission); + else + client.RemovePermission(thisPermission); - // return true; - // }; + GameMain.Server.UpdateClientPermissions(client); + + return true; + }; - // y += 20; - // if (y >= permissionsBox.Rect.Height -20) - // { - // y = 0; - // x += 100; - // } - //} + y += 20; + if (y >= permissionsBox.Rect.Height-40) + { + y = 0; + x += 100; + } + } } if (GameMain.Server != null || GameMain.Client.HasPermission(ClientPermissions.Kick)) From 03e55a2040766cbc8071d190ee75f1b9b53d7ec0 Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 21 Nov 2016 18:00:47 +0200 Subject: [PATCH 49/57] whoops --- Subsurface/Source/Screens/EditMapScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index bd26e75ec..3c04b411b 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -127,10 +127,10 @@ namespace Barotrauma topPanel.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); hullVolumeFrame = new GUIFrame(new Rectangle(145, 26, 280, 70), GUI.Style, topPanel); + hullVolumeFrame.Visible = false; hullVolumeFrame.Padding = new Vector4(3.0f, 3.0f, 3.0f, 3.0f); - GUITextBlock totalHullVolume = new GUITextBlock(new Rectangle(0, 0, 0, 20), "", GUI.Style, hullVolumeFrame, GUI.SmallFont); - totalHullVolume.Visible = false; + GUITextBlock totalHullVolume = new GUITextBlock(new Rectangle(0, 0, 0, 20), "", GUI.Style, hullVolumeFrame, GUI.SmallFont); totalHullVolume.TextGetter = GetTotalHullVolume; GUITextBlock selectedHullVolume = new GUITextBlock(new Rectangle(0, 30, 0, 20), "", GUI.Style, hullVolumeFrame, GUI.SmallFont); From 219f9258e2779095e95f853b5f6990144566b8c4 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 22 Nov 2016 17:16:59 +0200 Subject: [PATCH 50/57] v0.5.4.0 + converting parameters of the spawn command to correct case --- StyleCop.Cache | 7 ------ Subsurface/Properties/AssemblyInfo.cs | 4 +-- Subsurface/Source/DebugConsole.cs | 7 +++++- Subsurface/changelog.txt | 35 +++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) delete mode 100644 StyleCop.Cache diff --git a/StyleCop.Cache b/StyleCop.Cache deleted file mode 100644 index 472d28838..000000000 --- a/StyleCop.Cache +++ /dev/null @@ -1,7 +0,0 @@ - - 12 - - - - - \ No newline at end of file diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs index eace486ef..0faf1b81e 100644 --- a/Subsurface/Properties/AssemblyInfo.cs +++ b/Subsurface/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.5.3.4")] -[assembly: AssemblyFileVersion("0.5.3.4")] +[assembly: AssemblyVersion("0.5.4.0")] +[assembly: AssemblyFileVersion("0.5.4.0")] diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index a505c9da7..16bb1b362 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -292,6 +292,8 @@ namespace Barotrauma spawnPoint = WayPoint.GetRandom(commands[1].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); } + if (string.IsNullOrWhiteSpace(commands[1])) return; + if (spawnPoint != null) spawnPosition = spawnPoint.WorldPosition; if (commands[1].ToLowerInvariant()=="human") @@ -311,7 +313,10 @@ namespace Barotrauma } else { - spawnedCharacter = Character.Create("Content/Characters/" + commands[1] + "/" + commands[1] + ".xml", spawnPosition); + spawnedCharacter = Character.Create( + "Content/Characters/" + + commands[1].First().ToString().ToUpper() + commands[1].Substring(1) + + "/" + commands[1].ToLower() + ".xml", spawnPosition); } if (spawnedCharacter != null && GameMain.Server != null) diff --git a/Subsurface/changelog.txt b/Subsurface/changelog.txt index b308dafde..b13ee2981 100644 --- a/Subsurface/changelog.txt +++ b/Subsurface/changelog.txt @@ -1,3 +1,38 @@ +--------------------------------------------------------------------------------------------------------- +v0.5.4.0 +--------------------------------------------------------------------------------------------------------- + +Submarine editor: + - copy, paste and cut functionality + - items/structures can be copied by holding ctrl while dragging + - it's possible to move a wire by moving both items it's connected to (without having to move each + individual point of the wire separately) + - "hull volume helper" which makes it easier to select a suitable ballast tank size and + NeutralBallastLevel setting in the navigation terminal + - equipped items are removed when switching from wiring mode to character mode or vice versa + - no need to wait when deattaching items from the walls with a wrench + + +Bugfixes: + - wires are now positioned correctly in mirrored subs + - UI elements (buttons, textboxes, etc) can't be clicked through each other anymore + - fixed a bug that caused crashes when deattaching items from walls + - fixed a game-crashing particle bug + - fixed respawned characters getting assigned to a different team than the rest of the characters + (causing them to be displayed separately in the crew menu) + - pathfinding/autopilot fixes + +Misc: + - server hosts can give players special privileges (kick, ban, end round) + - saving the contents of the server info box and the traitor setting + - changes to battery logic: they can now be used to cover the entire power consumption of the + electrical grid (assuming their maximum output is high enough) + - added "artifact holders" to alien ruins (which can also be used for turning artifacts into power + sources if installed in a sub) + - changes to character collider behavior: crouching changes the size of the collider and it's + easier to step over small obstacles + + --------------------------------------------------------------------------------------------------------- v0.5.3.4 --------------------------------------------------------------------------------------------------------- From 4ad60e77a9f835acbcb8b93782829466d1bbc87d Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 22 Nov 2016 18:06:52 +0200 Subject: [PATCH 51/57] Item.SetContainedItemPositions recursively sets the positions of the items contained inside the contained items (and so on) --- Subsurface/Source/Items/Item.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 59dd3a653..7172dd548 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -600,7 +600,7 @@ namespace Barotrauma { contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f); } - + contained.Rect = new Rectangle( (int)(displayPos.X - contained.Rect.Width / 2.0f), @@ -609,6 +609,8 @@ namespace Barotrauma contained.Submarine = Submarine; contained.CurrentHull = CurrentHull; + + contained.SetContainedItemPositions(); } } From e39ab10a71eeaf6792b0ed982e4a4244f38f61eb Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 23 Nov 2016 17:52:23 +0200 Subject: [PATCH 52/57] Level generation fixes --- Subsurface/Source/Map/Levels/Level.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Subsurface/Source/Map/Levels/Level.cs b/Subsurface/Source/Map/Levels/Level.cs index e81bf75e3..a48a4ca3d 100644 --- a/Subsurface/Source/Map/Levels/Level.cs +++ b/Subsurface/Source/Map/Levels/Level.cs @@ -187,11 +187,11 @@ namespace Barotrauma minWidth = Math.Max(minWidth, 6500.0f); startPosition = new Vector2( - Rand.Range(minWidth * 2, minWidth * 4, false), + Rand.Range(minWidth, minWidth * 2, false), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false)); endPosition = new Vector2( - borders.Width - Rand.Range(minWidth * 2, minWidth * 4, false), + borders.Width - Rand.Range(minWidth, minWidth * 2, false), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false)); List pathNodes = new List(); @@ -211,8 +211,12 @@ namespace Barotrauma pathNodes.Add(new Vector2(endPosition.X, borders.Height)); - List> smallTunnels = new List>(); + if (pathNodes.Count <= 2) + { + pathNodes.Add((startPosition + endPosition) / 2); + } + List> smallTunnels = new List>(); for (int i = 0; i < generationParams.SmallTunnelCount; i++) { var tunnelStartPos = pathNodes[Rand.Range(2, pathNodes.Count - 2, false)]; @@ -421,7 +425,8 @@ namespace Barotrauma cellGrid[x, y].Add(cell); } - + + ruins = new List(); for (int i = 0; i(); ruins.Add(ruin); ruin.RuinShapes.Sort((shape1, shape2) => shape2.DistanceFromEntrance.CompareTo(shape1.DistanceFromEntrance)); From b773413b45c99a84bf80ac7587c0a81a34457c96 Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 23 Nov 2016 20:34:41 +0200 Subject: [PATCH 53/57] Fixed items disappearing from copypasted containers if the items have been removed from the original container, cloning links between entities --- Subsurface/Source/Items/Item.cs | 19 +++++++++ Subsurface/Source/Map/MapEntity.cs | 67 ++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 7172dd548..1b26fcc33 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1983,6 +1983,25 @@ namespace Barotrauma return true; } + public override void ShallowRemove() + { + base.ShallowRemove(); + + Removed = true; + + foreach (ItemComponent ic in components) + { + ic.Remove(); + } + ItemList.Remove(this); + + if (body != null) + { + body.Remove(); + body = null; + } + } + public override void Remove() { base.Remove(); diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 2b0a27b98..a8739c223 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -240,6 +240,17 @@ namespace Barotrauma Debug.Assert(clones.Count == entitiesToClone.Count); + //clone links between the entities + for (int i = 0; i < clones.Count; i++) + { + foreach (MapEntity linked in entitiesToClone[i].linkedTo) + { + if (!entitiesToClone.Contains(linked)) continue; + + clones[i].linkedTo.Add(clones[entitiesToClone.IndexOf(linked)]); + } + } + //connect clone wires to the clone items for (int i = 0; i < clones.Count; i++) { @@ -300,6 +311,18 @@ namespace Barotrauma public virtual void DrawDamage(SpriteBatch spriteBatch, Effect damageEffect) {} + ///

+ /// Remove the entity from the entity list without removing links to other entities + /// + public virtual void ShallowRemove() + { + base.Remove(); + + mapEntityList.Remove(this); + + if (aiTarget != null) aiTarget.Remove(); + } + public override void Remove() { base.Remove(); @@ -388,12 +411,13 @@ namespace Barotrauma if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.C) && PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.C)) { - copiedList = new List(selectedList); + CopyEntities(selectedList); } else if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.X) && PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.X)) { - copiedList = new List(selectedList); + CopyEntities(selectedList); + selectedList.ForEach(e => e.Remove()); selectedList.Clear(); } @@ -636,6 +660,24 @@ namespace Barotrauma selectedList.Add(entity); } + + /// + /// copies a list of entities to the "clipboard" (copiedList) + /// + private static void CopyEntities(List entities) + { + List prevEntities = new List(mapEntityList); + + copiedList = Clone(entities); + + //find all new entities created during cloning + var newEntities = mapEntityList.Except(prevEntities).ToList(); + + //do a "shallow remove" (removes the entities from the game without removing links between them) + // -> items will stay in their containers + newEntities.ForEach(e => e.ShallowRemove()); + } + public virtual void FlipX() { if (Submarine == null) @@ -752,26 +794,7 @@ namespace Barotrauma } } } - - public static List FindMapEntities(Vector2 pos) - { - List foundEntities = new List(); - foreach (MapEntity e in mapEntityList) - { - if (Submarine.RectContains(e.rect, pos)) foundEntities.Add(e); - } - return foundEntities; - } - - public static MapEntity FindMapEntity(Vector2 pos) - { - foreach (MapEntity e in mapEntityList) - { - if (Submarine.RectContains(e.rect, pos)) return e; - } - return null; - } - + /// /// Find entities whose rect intersects with the "selection rect" /// From 9061f0e53a1c97575d4db2417c7ffc0800a432ea Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 23 Nov 2016 21:20:00 +0200 Subject: [PATCH 54/57] Water flows more slowly through partially open gaps --- Subsurface/Source/Map/Gap.cs | 12 +++++------- Subsurface/Source/Map/Structure.cs | 7 +++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 3d346aaa6..b99a83ddf 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -252,7 +252,7 @@ namespace Barotrauma lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime); - if (LerpedFlowForce.Length() > 100.0f && flowTargetHull != null && flowTargetHull.Volume < flowTargetHull.FullVolume) + if (LerpedFlowForce.LengthSquared() > 10000.0f && flowTargetHull != null && flowTargetHull.Volume < flowTargetHull.FullVolume) { //UpdateFlowForce(); @@ -531,7 +531,7 @@ namespace Barotrauma //a variable affecting the water flow through the gap //the larger the gap is, the faster the water flows - float sizeModifier = size * open; + float sizeModifier = size * open * open; float delta = Hull.MaxCompress * sizeModifier * deltaTime; @@ -562,12 +562,10 @@ namespace Barotrauma if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress && hull1.Surface < rect.Y) { - - if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) { float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f; - + vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f); hull1.WaveVel[hull1.WaveY.Length - 1] += vel; hull1.WaveVel[hull1.WaveY.Length - 2] += vel; @@ -575,11 +573,11 @@ namespace Barotrauma else { float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 0.1f; - + vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f); hull1.WaveVel[0] += vel; hull1.WaveVel[1] += vel; - } + } } else { diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 966bff8c9..46d56a1bd 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -763,11 +763,10 @@ namespace Barotrauma if(CastShadow) GenerateConvexHull(); } + + sections[sectionIndex].gap.Open = (damage / prefab.MaxHealth - 0.5f) * 2.0f; } - - if (sections[sectionIndex].gap != null) - sections[sectionIndex].gap.Open = (damage/prefab.MaxHealth - 0.5f)*2; - + bool hadHole = SectionBodyDisabled(sectionIndex); sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.MaxHealth); From 9d1d64901aa7f5bdae733c927dfa7aa4447848b7 Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 23 Nov 2016 22:20:20 +0200 Subject: [PATCH 55/57] Pathfinding fixes: - determining if a character is close enough to a waypoint works now, even if the character is too short for its collider to overlap with the wp (e.g. crawlers) - enemies can drop down from platforms - an extra waypoint is placed at the middle of stairs to prevent characters from choosing a waypoint on a platform above the stairs as the starting point of their path --- .../Source/Characters/AI/EnemyAIController.cs | 45 ++++++++++++++++++- .../Characters/AI/IndoorsSteeringManager.cs | 4 +- .../Animation/FishAnimController.cs | 4 +- Subsurface/Source/Map/WayPoint.cs | 8 ++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index 22d8b5fe6..a470aafcb 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -110,7 +110,21 @@ namespace Barotrauma { UpdateDistanceAccumulator(); - Character.AnimController.IgnorePlatforms = (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); + bool ignorePlatforms = (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); + + if (steeringManager is IndoorsSteeringManager) + { + var currPath = ((IndoorsSteeringManager)steeringManager).CurrentPath; + if (currPath != null && currPath.CurrentNode != null) + { + if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y) + { + ignorePlatforms = true; + } + } + } + + Character.AnimController.IgnorePlatforms = ignorePlatforms; if (Character.AnimController is HumanoidAnimController) { @@ -501,13 +515,42 @@ namespace Barotrauma } spriteBatch.DrawString(GUI.Font, targetValue.ToString(), pos - Vector2.UnitY*20.0f, Color.Red); + } + if (selectedAiTarget != null) + { + GUI.DrawLine(spriteBatch, + new Vector2(Character.DrawPosition.X, -Character.DrawPosition.Y), + new Vector2(selectedAiTarget.WorldPosition.X, -selectedAiTarget.WorldPosition.Y), Color.Red); } spriteBatch.DrawString(GUI.Font, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, Color.Red); spriteBatch.DrawString(GUI.Font, "updatetargets: "+updateTargetsTimer, pos - Vector2.UnitY * 100.0f, Color.Red); spriteBatch.DrawString(GUI.Font, "cooldown: " + coolDownTimer, pos - Vector2.UnitY * 120.0f, Color.Red); + + + IndoorsSteeringManager pathSteering = steeringManager as IndoorsSteeringManager; + if (pathSteering == null || pathSteering.CurrentPath == null || pathSteering.CurrentPath.CurrentNode == null) return; + + GUI.DrawLine(spriteBatch, + new Vector2(Character.DrawPosition.X, -Character.DrawPosition.Y), + new Vector2(pathSteering.CurrentPath.CurrentNode.DrawPosition.X, -pathSteering.CurrentPath.CurrentNode.DrawPosition.Y), + Color.LightGreen); + + + for (int i = 1; i < pathSteering.CurrentPath.Nodes.Count; i++) + { + GUI.DrawLine(spriteBatch, + new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y), + new Vector2(pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i - 1].DrawPosition.Y), + Color.LightGreen); + + spriteBatch.DrawString(GUI.SmallFont, + pathSteering.CurrentPath.Nodes[i].ID.ToString(), + new Vector2(pathSteering.CurrentPath.Nodes[i].DrawPosition.X, -pathSteering.CurrentPath.Nodes[i].DrawPosition.Y - 10), + Color.LightGreen); + } } public override void FillNetworkData(NetBuffer message) diff --git a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs index 883022f5f..462a7e617 100644 --- a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs @@ -146,9 +146,11 @@ namespace Barotrauma } var collider = character.AnimController.Collider; + Vector2 colliderBottom = character.AnimController.GetColliderBottom(); if (Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X) < collider.radius * 2 && - Math.Abs(collider.SimPosition.Y - currentPath.CurrentNode.SimPosition.Y) < collider.height / 2 + collider.radius) + currentPath.CurrentNode.SimPosition.Y > colliderBottom.Y && + currentPath.CurrentNode.SimPosition.Y < colliderBottom.Y + 1.5f) { currentPath.SkipToNextNode(); } diff --git a/Subsurface/Source/Characters/Animation/FishAnimController.cs b/Subsurface/Source/Characters/Animation/FishAnimController.cs index 5547e9a70..ee420f416 100644 --- a/Subsurface/Source/Characters/Animation/FishAnimController.cs +++ b/Subsurface/Source/Characters/Animation/FishAnimController.cs @@ -225,9 +225,7 @@ namespace Barotrauma { movement = MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, 0.2f); if (movement == Vector2.Zero) return; - - IgnorePlatforms = (TargetMovement.Y < -Math.Abs(TargetMovement.X)); - + float mainLimbHeight, mainLimbAngle; if (MainLimb.type == LimbType.Torso) { diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index 38da4ef63..769a655bd 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -444,7 +444,7 @@ namespace Barotrauma foreach (Structure stairs in stairList) { - WayPoint[] stairPoints = new WayPoint[2]; + WayPoint[] stairPoints = new WayPoint[3]; stairPoints[0] = new WayPoint( new Vector2(stairs.Rect.X - 32.0f, @@ -463,8 +463,10 @@ namespace Barotrauma stairPoints[i].ConnectTo(closest); } } - - stairPoints[0].ConnectTo(stairPoints[1]); + + stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position)/2, SpawnType.Path, submarine); + stairPoints[0].ConnectTo(stairPoints[2]); + stairPoints[2].ConnectTo(stairPoints[1]); } foreach (Item item in Item.ItemList) From baf6a94e38bd6fc8fda6a8ed58cc91cb307a86e0 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 24 Nov 2016 19:15:38 +0200 Subject: [PATCH 56/57] Fixed wall damage not being visible if there's only one damaged section --- Subsurface/Source/Map/Structure.cs | 5 ++--- Subsurface/Source/Map/Submarine.cs | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 46d56a1bd..b79716e4a 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -488,7 +488,6 @@ namespace Barotrauma Draw(spriteBatch, false, false, damageEffect); } - private static float prevCutoff; private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null) { @@ -527,14 +526,14 @@ namespace Barotrauma { float newCutoff = Math.Min((sections[i].damage / prefab.MaxHealth), 0.65f); - if (Math.Abs(newCutoff - prevCutoff) > 0.01f) + if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f) { damageEffect.Parameters["aCutoff"].SetValue(newCutoff); damageEffect.Parameters["cCutoff"].SetValue(newCutoff * 1.2f); damageEffect.CurrentTechnique.Passes[0].Apply(); - prevCutoff = newCutoff; + Submarine.DamageEffectCutoff = newCutoff; } } diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 9afaa82c8..74ae8cbff 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -373,6 +373,9 @@ namespace Barotrauma } } + + public static float DamageEffectCutoff; + public static void DrawDamageable(SpriteBatch spriteBatch, Effect damageEffect, bool editing = false) { var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList; @@ -386,6 +389,8 @@ namespace Barotrauma { damageEffect.Parameters["aCutoff"].SetValue(0.0f); damageEffect.Parameters["cCutoff"].SetValue(0.0f); + + DamageEffectCutoff = 0.0f; } } From 12f98268b5c7b5dc4c239820add65782579ba2e8 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 24 Nov 2016 19:17:49 +0200 Subject: [PATCH 57/57] Increased character visibility range (how far monsters can see characters from), enemies manually steer towards their target if their steering path is finished or unreachable, mantis tweaking --- Subsurface/Content/Characters/Mantis/mantis.xml | 6 +++--- Subsurface/Source/Characters/AI/EnemyAIController.cs | 10 ++++++++++ Subsurface/Source/Characters/Character.cs | 4 +--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Subsurface/Content/Characters/Mantis/mantis.xml b/Subsurface/Content/Characters/Mantis/mantis.xml index cfcd6f781..669325144 100644 --- a/Subsurface/Content/Characters/Mantis/mantis.xml +++ b/Subsurface/Content/Characters/Mantis/mantis.xml @@ -9,7 +9,7 @@ - + @@ -89,6 +89,6 @@ + attackcooldown="1.0"/> diff --git a/Subsurface/Source/Characters/AI/EnemyAIController.cs b/Subsurface/Source/Characters/AI/EnemyAIController.cs index a470aafcb..21ad55bf9 100644 --- a/Subsurface/Source/Characters/AI/EnemyAIController.cs +++ b/Subsurface/Source/Characters/AI/EnemyAIController.cs @@ -251,6 +251,16 @@ namespace Barotrauma if (attackLimb != null) { steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition)); + + if (steeringManager is IndoorsSteeringManager) + { + var indoorsSteering = (IndoorsSteeringManager)steeringManager; + if (indoorsSteering.CurrentPath!=null && (indoorsSteering.CurrentPath.Finished || indoorsSteering.CurrentPath.Unreachable)) + { + steeringManager.SteeringManual(deltaTime, attackSimPosition - attackLimb.SimPosition); + } + } + if (attackingLimb != null) UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition); } } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 895f9933d..1b5dd2d71 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1357,9 +1357,7 @@ namespace Barotrauma { if (aiTarget == null) return; - aiTarget.SightRange = 0.0f; - - aiTarget.SightRange = Mass*10.0f + AnimController.Collider.LinearVelocity.Length()*500.0f; + aiTarget.SightRange = Mass*100.0f + AnimController.Collider.LinearVelocity.Length()*500.0f; } public void ShowSpeechBubble(float duration, Color color)