From 948285f6abfdee1f28a297547c0391f557fa2c9a Mon Sep 17 00:00:00 2001 From: Regalis Date: Wed, 28 Oct 2015 19:07:17 +0200 Subject: [PATCH] Control settings, more server settings, selecting character face --- Launcher2/LauncherMain.cs | 2 +- Subsurface/Barotrauma.csproj | 2 +- Subsurface/Content/Items/Button/button.xml | 4 +- Subsurface/Content/Items/Door/doors.xml | 4 +- .../Content/Items/Electricity/lights.xml | 2 +- .../Content/Items/Electricity/poweritems.xml | 6 +- .../Content/Items/Electricity/signalitems.xml | 32 +-- Subsurface/Content/Items/Engine/engine.xml | 6 +- Subsurface/Content/Items/MiniMap/item.xml | 2 +- .../Items/OxygenGenerator/oxygengenerator.xml | 2 +- Subsurface/Content/Items/Pump/pump.xml | 2 +- Subsurface/Content/Items/Reactor/reactor.xml | 2 +- .../Content/Items/Weapons/explosives.xml | 4 +- Subsurface/Content/UI/style.xml | 14 +- Subsurface/Source/Characters/Character.cs | 137 +++++++----- Subsurface/Source/Characters/CharacterInfo.cs | 19 +- Subsurface/Source/ContentPackage.cs | 3 + Subsurface/Source/DebugConsole.cs | 4 +- Subsurface/Source/EventInput/EventInput.cs | 4 +- Subsurface/Source/GUI/GUIComponent.cs | 7 +- Subsurface/Source/GUI/GUIDropDown.cs | 6 + Subsurface/Source/GUI/GUIScrollBar.cs | 2 +- Subsurface/Source/GUI/GUITextBlock.cs | 10 +- Subsurface/Source/GUI/GUITextBox.cs | 64 ++++-- Subsurface/Source/GUI/GUITickBox.cs | 2 +- Subsurface/Source/GameMain.cs | 2 +- Subsurface/Source/GameSettings.cs | 195 +++++++++++++++--- .../Items/Components/Holdable/Holdable.cs | 4 +- .../Items/Components/Holdable/MeleeWeapon.cs | 16 +- .../Items/Components/Holdable/RangedWeapon.cs | 2 +- .../Items/Components/Holdable/RepairTool.cs | 4 +- .../Items/Components/Holdable/Throwable.cs | 6 +- .../Items/Components/Machines/Engine.cs | 2 + .../Source/Items/Components/Projectile.cs | 2 +- .../Components/Signal/ConnectionPanel.cs | 2 +- Subsurface/Source/Items/Item.cs | 18 +- Subsurface/Source/Map/WayPoint.cs | 4 +- Subsurface/Source/Networking/GameServer.cs | 49 +---- Subsurface/Source/Networking/NetworkMember.cs | 22 +- Subsurface/Source/PlayerInput.cs | 161 ++++++++++++--- Subsurface/Source/Screens/MainMenuScreen.cs | 35 ++++ Subsurface/Source/Screens/NetLobbyScreen.cs | 56 +++-- Subsurface/Source/Screens/NetLobbySettings.cs | 144 +++++++++++++ Subsurface/Source/Screens/SettingsScreen.cs | 11 - Subsurface/Source/Utils/TextureLoader.cs | 37 +--- Subsurface_Solution.v12.suo | Bin 811008 -> 779776 bytes 46 files changed, 789 insertions(+), 325 deletions(-) create mode 100644 Subsurface/Source/Screens/NetLobbySettings.cs delete mode 100644 Subsurface/Source/Screens/SettingsScreen.cs diff --git a/Launcher2/LauncherMain.cs b/Launcher2/LauncherMain.cs index 595dbadc4..3c943314b 100644 --- a/Launcher2/LauncherMain.cs +++ b/Launcher2/LauncherMain.cs @@ -243,7 +243,7 @@ namespace Launcher2 return true; } - private bool ToggleFullScreen(object obj) + private bool ToggleFullScreen(GUITickBox tickBox) { settings.FullScreenEnabled = !settings.FullScreenEnabled; return true; diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index a298fc32a..3fadc6abb 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -103,6 +103,7 @@ + @@ -225,7 +226,6 @@ - diff --git a/Subsurface/Content/Items/Button/button.xml b/Subsurface/Content/Items/Button/button.xml index e89794e0d..2e487675d 100644 --- a/Subsurface/Content/Items/Button/button.xml +++ b/Subsurface/Content/Items/Button/button.xml @@ -15,12 +15,12 @@ - + - diff --git a/Subsurface/Content/Items/Door/doors.xml b/Subsurface/Content/Items/Door/doors.xml index 298acf04d..8c0819d0e 100644 --- a/Subsurface/Content/Items/Door/doors.xml +++ b/Subsurface/Content/Items/Door/doors.xml @@ -19,7 +19,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/Subsurface/Content/Items/Electricity/lights.xml b/Subsurface/Content/Items/Electricity/lights.xml index 85da571ab..2a4152f4e 100644 --- a/Subsurface/Content/Items/Electricity/lights.xml +++ b/Subsurface/Content/Items/Electricity/lights.xml @@ -11,7 +11,7 @@ - + diff --git a/Subsurface/Content/Items/Electricity/poweritems.xml b/Subsurface/Content/Items/Electricity/poweritems.xml index 52f63dcc0..8b1ce1590 100644 --- a/Subsurface/Content/Items/Electricity/poweritems.xml +++ b/Subsurface/Content/Items/Electricity/poweritems.xml @@ -17,7 +17,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -69,7 +69,7 @@ - + diff --git a/Subsurface/Content/Items/Electricity/signalitems.xml b/Subsurface/Content/Items/Electricity/signalitems.xml index 9e138f855..a18e6ee2b 100644 --- a/Subsurface/Content/Items/Electricity/signalitems.xml +++ b/Subsurface/Content/Items/Electricity/signalitems.xml @@ -80,13 +80,13 @@ - - + @@ -108,12 +108,12 @@ - - + @@ -135,12 +135,12 @@ - - + @@ -163,11 +163,11 @@ - + - + @@ -188,12 +188,12 @@ - - + @@ -212,12 +212,12 @@ - - + @@ -239,12 +239,12 @@ - - + @@ -264,12 +264,12 @@ - - + diff --git a/Subsurface/Content/Items/Engine/engine.xml b/Subsurface/Content/Items/Engine/engine.xml index a14c67887..c5d53d5df 100644 --- a/Subsurface/Content/Items/Engine/engine.xml +++ b/Subsurface/Content/Items/Engine/engine.xml @@ -13,7 +13,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -60,7 +60,7 @@ - + diff --git a/Subsurface/Content/Items/MiniMap/item.xml b/Subsurface/Content/Items/MiniMap/item.xml index 463c391d3..3a234dd87 100644 --- a/Subsurface/Content/Items/MiniMap/item.xml +++ b/Subsurface/Content/Items/MiniMap/item.xml @@ -10,7 +10,7 @@ - + diff --git a/Subsurface/Content/Items/OxygenGenerator/oxygengenerator.xml b/Subsurface/Content/Items/OxygenGenerator/oxygengenerator.xml index 1fc53de08..a70fabc8c 100644 --- a/Subsurface/Content/Items/OxygenGenerator/oxygengenerator.xml +++ b/Subsurface/Content/Items/OxygenGenerator/oxygengenerator.xml @@ -17,7 +17,7 @@ - + diff --git a/Subsurface/Content/Items/Pump/pump.xml b/Subsurface/Content/Items/Pump/pump.xml index 8ce2ecfdb..39e8d8c44 100644 --- a/Subsurface/Content/Items/Pump/pump.xml +++ b/Subsurface/Content/Items/Pump/pump.xml @@ -10,7 +10,7 @@ - + diff --git a/Subsurface/Content/Items/Reactor/reactor.xml b/Subsurface/Content/Items/Reactor/reactor.xml index 7ace14c21..a8bcb913a 100644 --- a/Subsurface/Content/Items/Reactor/reactor.xml +++ b/Subsurface/Content/Items/Reactor/reactor.xml @@ -31,7 +31,7 @@ - + diff --git a/Subsurface/Content/Items/Weapons/explosives.xml b/Subsurface/Content/Items/Weapons/explosives.xml index b5f5c269a..03eb44db1 100644 --- a/Subsurface/Content/Items/Weapons/explosives.xml +++ b/Subsurface/Content/Items/Weapons/explosives.xml @@ -27,7 +27,7 @@ - + @@ -35,7 +35,7 @@ - + diff --git a/Subsurface/Content/UI/style.xml b/Subsurface/Content/UI/style.xml index 093d47574..7e6bf5a62 100644 --- a/Subsurface/Content/UI/style.xml +++ b/Subsurface/Content/UI/style.xml @@ -31,7 +31,6 @@ @@ -39,16 +38,23 @@ + + + + diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 23968f6ad..3040be6e6 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -286,18 +286,24 @@ namespace Barotrauma { keys = new Key[Enum.GetNames(typeof(InputType)).Length]; - keys[(int)InputType.Select] = new Key(false); - keys[(int)InputType.ActionHeld] = new Key(true); - keys[(int)InputType.ActionHit] = new Key(false); - keys[(int)InputType.SecondaryHit] = new Key(false); - keys[(int)InputType.SecondaryHeld] = new Key(true); - keys[(int)InputType.Left] = new Key(true); - keys[(int)InputType.Right] = new Key(true); - keys[(int)InputType.Up] = new Key(true); - keys[(int)InputType.Down] = new Key(true); + for (int i = 0; i < Enum.GetNames(typeof(InputType)).Length; i++) + { + keys[i] = new Key(GameMain.Config.KeyBind((InputType)i)); + } - keys[(int)InputType.Run] = new Key(true); + //keys[(int)InputType.Select] = new Key(GameMain.Config.KeyBind(InputType.Select)); + //keys[(int)InputType.ActionHeld] = new Key(true); + //keys[(int)InputType.ActionHit] = new Key(false); + //keys[(int)InputType.SecondaryHit] = new Key(false); + //keys[(int)InputType.SecondaryHeld] = new Key(true); + + //keys[(int)InputType.Left] = new Key(true); + //keys[(int)InputType.Right] = new Key(true); + //keys[(int)InputType.Up] = new Key(true); + //keys[(int)InputType.Down] = new Key(true); + + //keys[(int)InputType.Run] = new Key(true); selectedItems = new Item[2]; @@ -410,16 +416,22 @@ namespace Barotrauma } } - public bool GetInputState(InputType inputType) + public bool IsKeyHit(InputType inputType) { - return keys[(int)inputType].State; + return keys[(int)inputType].Hit; + } + + public bool IsKeyDown(InputType inputType) + { + return keys[(int)inputType].Held; } public void ClearInputs() { foreach (Key key in keys) { - key.State = false; + key.Hit = false; + key.Held = false; } } @@ -477,10 +489,10 @@ namespace Barotrauma if (isDead || AnimController.StunTimer>0.0f) return; Vector2 targetMovement = Vector2.Zero; - if (GetInputState(InputType.Left)) targetMovement.X -= 1.0f; - if (GetInputState(InputType.Right)) targetMovement.X += 1.0f; - if (GetInputState(InputType.Up)) targetMovement.Y += 1.0f; - if (GetInputState(InputType.Down)) targetMovement.Y -= 1.0f; + if (IsKeyDown(InputType.Left)) targetMovement.X -= 1.0f; + if (IsKeyDown(InputType.Right)) targetMovement.X += 1.0f; + if (IsKeyDown(InputType.Up)) targetMovement.Y += 1.0f; + if (IsKeyDown(InputType.Down)) targetMovement.Y -= 1.0f; //the vertical component is only used for falling through platforms and climbing ladders when not in water, //so the movement can't be normalized or the character would walk slower when pressing down/up @@ -490,7 +502,7 @@ namespace Barotrauma if (length > 0.0f) targetMovement = targetMovement / length; } - if (Math.Sign(targetMovement.X) == Math.Sign(AnimController.Dir) && GetInputState(InputType.Run)) + if (Math.Sign(targetMovement.X) == Math.Sign(AnimController.Dir) && IsKeyDown(InputType.Run)) targetMovement *= 3.0f; targetMovement *= SpeedMultiplier; @@ -519,22 +531,22 @@ namespace Barotrauma { if (selectedItems[i] == null) continue; if (i == 1 && selectedItems[0] == selectedItems[1]) continue; - - if (GetInputState(InputType.ActionHeld)) selectedItems[i].Use(deltaTime, this); - if (GetInputState(InputType.SecondaryHeld) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this); + + if (IsKeyDown(InputType.Use)) selectedItems[i].Use(deltaTime, this); + if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this); } if (selectedConstruction != null) { - if (GetInputState(InputType.ActionHeld)) selectedConstruction.Use(deltaTime, this); - if (selectedConstruction != null && GetInputState(InputType.SecondaryHeld)) selectedConstruction.SecondaryUse(deltaTime, this); + if (IsKeyDown(InputType.Use)) selectedConstruction.Use(deltaTime, this); + if (selectedConstruction != null && IsKeyDown(InputType.Aim)) selectedConstruction.SecondaryUse(deltaTime, this); } if (IsNetworkPlayer) { foreach (Key key in keys) { - key.Reset(); + key.ResetHit(); } } } @@ -658,24 +670,30 @@ namespace Barotrauma if (!DisableControls) { - keys[(int)InputType.Left].SetState(PlayerInput.KeyDown(Keys.A)); - keys[(int)InputType.Right].SetState(PlayerInput.KeyDown(Keys.D)); - keys[(int)InputType.Up].SetState(PlayerInput.KeyDown(Keys.W)); - keys[(int)InputType.Down].SetState(PlayerInput.KeyDown(Keys.S)); + for (int i = 0; i < keys.Length; i++ ) + { + keys[i].SetState(); + } - keys[(int)InputType.Select].SetState(PlayerInput.KeyHit(Keys.E)); - keys[(int)InputType.ActionHit].SetState(PlayerInput.LeftButtonClicked()); - keys[(int)InputType.ActionHeld].SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed); - keys[(int)InputType.SecondaryHit].SetState(PlayerInput.RightButtonClicked()); - keys[(int)InputType.SecondaryHeld].SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed); + // keys[(int)InputType.Left].SetState(GameMain.Config.KeyBind(inpu)); + //keys[(int)InputType.Right].SetState(PlayerInput.KeyDown(Keys.D)); + //keys[(int)InputType.Up].SetState(PlayerInput.KeyDown(Keys.W)); + //keys[(int)InputType.Down].SetState(PlayerInput.KeyDown(Keys.S)); - keys[(int)InputType.Run].SetState(PlayerInput.KeyDown(Keys.LeftShift)); + //keys[(int)InputType.Select].SetState(PlayerInput.KeyHit(Keys.E)); + //keys[(int)InputType.ActionHit].SetState(PlayerInput.LeftButtonClicked()); + //keys[(int)InputType.ActionHeld].SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed); + //keys[(int)InputType.SecondaryHit].SetState(PlayerInput.RightButtonClicked()); + //keys[(int)InputType.SecondaryHeld].SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed); + + //keys[(int)InputType.Run].SetState(PlayerInput.KeyDown(Keys.LeftShift)); } else { foreach (Key key in keys) { - key.SetState(false); + if (key == null) continue; + key.Reset(); } } @@ -743,7 +761,12 @@ namespace Barotrauma if (closestItem.Pick(this)) { new NetworkEvent(NetworkEventType.PickItem, ID, true, - new int[] { closestItem.ID, GetInputState(InputType.Select) ? 1 : 0, GetInputState(InputType.ActionHit) ? 1 : 0 }); + new int[] + { + closestItem.ID, + IsKeyHit(InputType.Select) ? 1 : 0, + IsKeyHit(InputType.Use) ? 1 : 0 + }); } } } @@ -756,7 +779,7 @@ namespace Barotrauma } } - if (GetInputState(InputType.Select)) + if (IsKeyHit(InputType.Select)) { if (selectedCharacter != null) { @@ -1186,30 +1209,30 @@ namespace Barotrauma return true; case NetworkEventType.EntityUpdate: var hasInputs = - GetInputState(InputType.Left) || - GetInputState(InputType.Right) || - GetInputState(InputType.Up) || - GetInputState(InputType.Down) || - GetInputState(InputType.ActionHeld) || - GetInputState(InputType.SecondaryHeld); + IsKeyDown(InputType.Left) || + IsKeyDown(InputType.Right) || + IsKeyDown(InputType.Up) || + IsKeyDown(InputType.Down) || + IsKeyDown(InputType.Use) || + IsKeyDown(InputType.Aim); message.Write(hasInputs); message.Write((float)NetTime.Now); if (!hasInputs) return true; - message.Write(keys[(int)InputType.ActionHeld].Dequeue); + message.Write(keys[(int)InputType.Use].DequeueHeld); - bool secondaryHeld = keys[(int)InputType.SecondaryHeld].Dequeue; + bool secondaryHeld = keys[(int)InputType.Aim].DequeueHeld; message.Write(secondaryHeld); - message.Write(keys[(int)InputType.Left].Dequeue); - message.Write(keys[(int)InputType.Right].Dequeue); + message.Write(keys[(int)InputType.Left].Held); + message.Write(keys[(int)InputType.Right].Held); - message.Write(keys[(int)InputType.Up].Dequeue); - message.Write(keys[(int)InputType.Down].Dequeue); + message.Write(keys[(int)InputType.Up].Held); + message.Write(keys[(int)InputType.Down].Held); - message.Write(keys[(int)InputType.Run].Dequeue); + message.Write(keys[(int)InputType.Run].Held); if (secondaryHeld) { @@ -1386,18 +1409,18 @@ namespace Barotrauma AnimController.IsStanding = true; - keys[(int)InputType.ActionHeld].State = actionKeyState; - keys[(int)InputType.SecondaryHeld].State = secondaryKeyState; + keys[(int)InputType.Use].Held = actionKeyState; + keys[(int)InputType.Aim].Held = secondaryKeyState; if (sendingTime <= LastNetworkUpdate) return; - keys[(int)InputType.Left].State = leftKeyState; - keys[(int)InputType.Right].State = rightKeyState; + keys[(int)InputType.Left].Held = leftKeyState; + keys[(int)InputType.Right].Held = rightKeyState; - keys[(int)InputType.Up].State = upKeyState; - keys[(int)InputType.Down].State = downKeyState; + keys[(int)InputType.Up].Held = upKeyState; + keys[(int)InputType.Down].Held = downKeyState; - keys[(int)InputType.Run].State = runState; + keys[(int)InputType.Run].Held = runState; float dir = 1.0f; Vector2 pos = Vector2.Zero; diff --git a/Subsurface/Source/Characters/CharacterInfo.cs b/Subsurface/Source/Characters/CharacterInfo.cs index 4ed7af120..affe2a016 100644 --- a/Subsurface/Source/Characters/CharacterInfo.cs +++ b/Subsurface/Source/Characters/CharacterInfo.cs @@ -25,7 +25,7 @@ namespace Barotrauma public int Salary; - public int HeadSpriteId; + private int headSpriteId; private Sprite headSprite; public bool StartItemsGiven; @@ -44,6 +44,23 @@ namespace Barotrauma } } + public int HeadSpriteId + { + get { return headSpriteId; } + set + { + int oldId = headSpriteId; + + headSpriteId = value; + Vector2 spriteRange = headSpriteRange[gender == Gender.Male ? 0 : 1]; + + if (headSpriteId < (int)spriteRange.X) headSpriteId = (int)(spriteRange.Y-1); + if (headSpriteId > (int)spriteRange.Y) headSpriteId = (int)(spriteRange.X); + + if (headSpriteId != oldId) headSprite = null; + } + } + public Gender Gender { get { return gender; } diff --git a/Subsurface/Source/ContentPackage.cs b/Subsurface/Source/ContentPackage.cs index 61283bedd..826a376a7 100644 --- a/Subsurface/Source/ContentPackage.cs +++ b/Subsurface/Source/ContentPackage.cs @@ -173,6 +173,7 @@ namespace Barotrauma } return filePaths; } + public static void LoadAll(string folder) { if (!Directory.Exists(folder)) @@ -189,6 +190,8 @@ namespace Barotrauma string[] files = Directory.GetFiles(folder, "*.xml"); + list.Clear(); + foreach (string filePath in files) { ContentPackage package = new ContentPackage(filePath); diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 69945e1c4..d5d27bb5f 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -141,7 +141,7 @@ namespace Barotrauma public static void ExecuteCommand(string command, GameMain game) { #if !DEBUG - if (GameMain.Client!=null) + if (GameMain.Client != null) { ThrowError("Console commands are disabled in multiplayer mode"); return; @@ -216,7 +216,7 @@ namespace Barotrauma case "control": if (commands.Length < 2) break; commands[1] = commands[1].ToLower(); - Character.Controlled = Character.CharacterList.Find(c => c.Name.ToLower() == commands[1]); + Character.Controlled = Character.CharacterList.Find(c => !c.IsNetworkPlayer && c.Name.ToLower() == commands[1]); break; case "heal": if (Character.Controlled != null) diff --git a/Subsurface/Source/EventInput/EventInput.cs b/Subsurface/Source/EventInput/EventInput.cs index 331c8c5d2..d166c78c9 100644 --- a/Subsurface/Source/EventInput/EventInput.cs +++ b/Subsurface/Source/EventInput/EventInput.cs @@ -169,8 +169,7 @@ namespace EventInput public static void OnCharEntered(char character) { - if (CharEntered != null) - CharEntered(null, new CharacterEventArgs(character, 0)); + if (CharEntered != null) CharEntered(null, new CharacterEventArgs(character, 0)); } static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) @@ -186,6 +185,7 @@ namespace EventInput case WM_KEYDOWN: if (KeyDown != null) KeyDown(null, new KeyEventArgs((Keys)wParam)); + break; case WM_KEYUP: diff --git a/Subsurface/Source/GUI/GUIComponent.cs b/Subsurface/Source/GUI/GUIComponent.cs index 299c1404c..618f489ff 100644 --- a/Subsurface/Source/GUI/GUIComponent.cs +++ b/Subsurface/Source/GUI/GUIComponent.cs @@ -143,6 +143,11 @@ namespace Barotrauma set { selectedColor = value; } } + public static KeyboardDispatcher KeyboardDispatcher + { + get { return keyboardDispatcher; } + } + protected GUIComponent(GUIStyle style) { Visible = true; @@ -227,7 +232,7 @@ namespace Barotrauma foreach (Sprite sprite in sprites) { Vector2 startPos = new Vector2(rect.X, rect.Y); - Vector2 size = new Vector2(sprite.SourceRect.Width, sprite.SourceRect.Height); + Vector2 size = new Vector2(Math.Min(sprite.SourceRect.Width,rect.Width), Math.Min(sprite.SourceRect.Height,rect.Height)); if (sprite.size.X == 0.0f) size.X = rect.Width; if (sprite.size.Y == 0.0f) size.Y = rect.Height; diff --git a/Subsurface/Source/GUI/GUIDropDown.cs b/Subsurface/Source/GUI/GUIDropDown.cs index 73c1bcd02..01fd79766 100644 --- a/Subsurface/Source/GUI/GUIDropDown.cs +++ b/Subsurface/Source/GUI/GUIDropDown.cs @@ -9,6 +9,10 @@ namespace Barotrauma { public class GUIDropDown : GUIComponent { + + public delegate bool OnSelectedHandler(GUIComponent selected); + public OnSelectedHandler OnSelected; + private GUIButton button; private GUIListBox listBox; @@ -65,6 +69,8 @@ namespace Barotrauma Dropped = false; + if (OnSelected != null) OnSelected(component); + return true; } diff --git a/Subsurface/Source/GUI/GUIScrollBar.cs b/Subsurface/Source/GUI/GUIScrollBar.cs index 74d086110..57255549c 100644 --- a/Subsurface/Source/GUI/GUIScrollBar.cs +++ b/Subsurface/Source/GUI/GUIScrollBar.cs @@ -90,7 +90,7 @@ namespace Barotrauma parent.AddChild(this); isHorizontal = (rect.Width > rect.Height); - frame = new GUIFrame(new Rectangle(0,0,0,0), Color.White, style, this); + frame = new GUIFrame(new Rectangle(0,0,0,0), Color.Black*0.8f, style, this); //AddChild(frame); //System.Diagnostics.Debug.WriteLine(frame.rect); diff --git a/Subsurface/Source/GUI/GUITextBlock.cs b/Subsurface/Source/GUI/GUITextBlock.cs index ac4f79400..24050692c 100644 --- a/Subsurface/Source/GUI/GUITextBlock.cs +++ b/Subsurface/Source/GUI/GUITextBlock.cs @@ -1,5 +1,6 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using System; namespace Barotrauma { @@ -120,7 +121,7 @@ namespace Barotrauma this.alignment = alignment; this.textAlignment = textAlignment; - + if (parent != null) parent.AddChild(this); @@ -215,8 +216,8 @@ namespace Barotrauma Color currColor = color; if (state == ComponentState.Hover) currColor = hoverColor; if (state == ComponentState.Selected) currColor = selectedColor; - - GUI.DrawRectangle(spriteBatch, rect, currColor*(currColor.A/255.0f), true); + + if (currColor.A * currColor.A > 0.0f) GUI.DrawRectangle(spriteBatch, rect, currColor * (currColor.A / 255.0f), true); base.Draw(spriteBatch); @@ -233,6 +234,9 @@ namespace Barotrauma } DrawChildren(spriteBatch); + + if (OutlineColor.A * currColor.A > 0.0f) GUI.DrawRectangle(spriteBatch, rect, OutlineColor * (currColor.A / 255.0f), false); + } } } diff --git a/Subsurface/Source/GUI/GUITextBox.cs b/Subsurface/Source/GUI/GUITextBox.cs index 9187a8f37..cf31ade77 100644 --- a/Subsurface/Source/GUI/GUITextBox.cs +++ b/Subsurface/Source/GUI/GUITextBox.cs @@ -7,11 +7,11 @@ using Microsoft.Xna.Framework.Input; namespace Barotrauma { - delegate void TextBoxEvent(GUITextBox sender); + delegate void TextBoxEvent(GUITextBox sender, Keys key); class GUITextBox : GUIComponent, IKeyboardSubscriber { - public event TextBoxEvent Clicked; + public event TextBoxEvent OnSelected; bool caretVisible; float caretTimer; @@ -19,11 +19,13 @@ namespace Barotrauma GUITextBlock textBlock; public delegate bool OnEnterHandler(GUITextBox textBox, string text); - public OnEnterHandler OnEnter; + public OnEnterHandler OnEnterPressed; + + public event TextBoxEvent OnKeyHit; public delegate bool OnTextChangedHandler(GUITextBox textBox, string text); public OnTextChangedHandler OnTextChanged; - + public GUITextBlock.TextGetterHandler TextGetter { get { return textBlock.TextGetter; } @@ -68,6 +70,8 @@ namespace Barotrauma } } + public bool CaretEnabled; + public String Text { get @@ -97,6 +101,7 @@ namespace Barotrauma //ensure that text cannot be larger than the box Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1); } + } } } @@ -130,10 +135,13 @@ namespace Barotrauma parent.AddChild(this); textBlock = new GUITextBlock(new Rectangle(0,0,0,0), "", color, textColor, textAlignment, style, this); - textBlock.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f); + if (style != null) style.Apply(textBlock, this); + textBlock.Padding = new Vector4(3.0f, 0.0f, 3.0f, 0.0f); previousMouse = PlayerInput.GetMouseState; + + CaretEnabled = true; //SetTextPos(); } @@ -141,7 +149,7 @@ namespace Barotrauma { Selected = true; keyboardDispatcher.Subscriber = this; - if (Clicked != null) Clicked(this); + //if (Clicked != null) Clicked(this); } public void Deselect() @@ -158,27 +166,37 @@ namespace Barotrauma if (flashTimer > 0.0f) flashTimer -= deltaTime; if (!Enabled) return; - caretTimer += deltaTime; - caretVisible = ((caretTimer*1000.0f) % 1000) < 500; + if (CaretEnabled) + { + caretTimer += deltaTime; + caretVisible = ((caretTimer*1000.0f) % 1000) < 500; + } if (rect.Contains(PlayerInput.MousePosition)) { - state = ComponentState.Hover; - if (PlayerInput.LeftButtonClicked()) Select(); + state = ComponentState.Hover; + if (PlayerInput.LeftButtonClicked()) + { + Select(); + if (OnSelected != null) OnSelected(this, Keys.None); + } } else { state = ComponentState.None; + } + textBlock.State = state; + if (keyboardDispatcher.Subscriber == this) { Character.DisableControls = true; - if (OnEnter != null && PlayerInput.KeyHit(Keys.Enter)) + if (OnEnterPressed != null && PlayerInput.KeyHit(Keys.Enter)) { string input = Text; Text = ""; - OnEnter(this, input); + OnEnterPressed(this, input); } } @@ -191,6 +209,8 @@ namespace Barotrauma if (!Visible) return; DrawChildren(spriteBatch); + + if (!CaretEnabled) return; Vector2 caretPos = textBlock.CaretPos; @@ -224,14 +244,14 @@ namespace Barotrauma if (Text.Length > 0) Text = Text.Substring(0, Text.Length - 1); break; - case '\r': //return - if (OnEnterPressed != null) - OnEnterPressed(this); - break; - case '\t': //tab - if (OnTabPressed != null) - OnTabPressed(this); - break; + //case '\r': //return + // if (OnEnterPressed != null) + // OnEnterPressed(this); + // break; + //case '\t': //tab + // if (OnTabPressed != null) + // OnTabPressed(this); + // break; } if (OnTextChanged != null) OnTextChanged(this, Text); @@ -239,10 +259,10 @@ namespace Barotrauma public void ReceiveSpecialInput(Keys key) { + if (OnKeyHit != null) OnKeyHit(this, key); } - public event TextBoxEvent OnEnterPressed; - public event TextBoxEvent OnTabPressed; + //public event TextBoxEvent OnTabPressed; public bool Selected { diff --git a/Subsurface/Source/GUI/GUITickBox.cs b/Subsurface/Source/GUI/GUITickBox.cs index f63ffcf10..49c315044 100644 --- a/Subsurface/Source/GUI/GUITickBox.cs +++ b/Subsurface/Source/GUI/GUITickBox.cs @@ -9,7 +9,7 @@ namespace Barotrauma GUIFrame box; GUITextBlock text; - public delegate bool OnSelectedHandler(object obj); + public delegate bool OnSelectedHandler(GUITickBox obj); public OnSelectedHandler OnSelected; private bool selected; diff --git a/Subsurface/Source/GameMain.cs b/Subsurface/Source/GameMain.cs index 119f8c918..73f08e5f7 100644 --- a/Subsurface/Source/GameMain.cs +++ b/Subsurface/Source/GameMain.cs @@ -69,7 +69,7 @@ namespace Barotrauma //private Stopwatch renderTimer; //public static int renderTimeElapsed; - + public Camera Cam { get { return GameScreen.Cam; } diff --git a/Subsurface/Source/GameSettings.cs b/Subsurface/Source/GameSettings.cs index c6f728918..d24c8a73d 100644 --- a/Subsurface/Source/GameSettings.cs +++ b/Subsurface/Source/GameSettings.cs @@ -12,10 +12,15 @@ namespace Barotrauma public class GameSettings { private GUIFrame settingsFrame; + private GUIButton applyButton; private float soundVolume, musicVolume; - private Keys[] keyMapping; + private KeyOrMouse[] keyMapping; + + + private bool unsavedSettings; + public GUIFrame SettingsFrame { @@ -26,6 +31,11 @@ namespace Barotrauma } } + public KeyOrMouse KeyBind(InputType inputType) + { + return keyMapping[(int)inputType]; + } + public int GraphicsWidth { get; set; } public int GraphicsHeight { get; set; } @@ -37,6 +47,19 @@ namespace Barotrauma public bool AutoCheckUpdates { get; set; } public bool WasGameUpdated { get; set; } + public bool UnsavedSettings + { + get + { + return unsavedSettings; + } + private set + { + unsavedSettings = value; + if (applyButton != null) applyButton.Selected = unsavedSettings; + } + } + public float SoundVolume { get { return soundVolume; } @@ -59,6 +82,8 @@ namespace Barotrauma public GameSettings(string filePath) { + ContentPackage.LoadAll(ContentPackage.Folder); + Load(filePath); } @@ -79,7 +104,7 @@ namespace Barotrauma return; } - + XElement graphicsMode = doc.Root.Element("graphicsmode"); GraphicsWidth = ToolBox.GetAttributeInt(graphicsMode, "width", 0); GraphicsHeight = ToolBox.GetAttributeInt(graphicsMode, "height", 0); @@ -100,14 +125,17 @@ namespace Barotrauma SoundVolume = ToolBox.GetAttributeFloat(doc.Root, "soundvolume", 1.0f); MusicVolume = ToolBox.GetAttributeFloat(doc.Root, "musicvolume", 0.3f); - keyMapping = new Keys[Enum.GetNames(typeof(InputType)).Length]; - keyMapping[(int)InputType.Up] = Keys.W; - keyMapping[(int)InputType.Down] = Keys.S; - keyMapping[(int)InputType.Left] = Keys.A; - keyMapping[(int)InputType.Right] = Keys.D; - keyMapping[(int)InputType.Run] = Keys.LeftShift; - keyMapping[(int)InputType.Chat] = Keys.Tab; - keyMapping[(int)InputType.Select] = Keys.E; + keyMapping = new KeyOrMouse[Enum.GetNames(typeof(InputType)).Length]; + keyMapping[(int)InputType.Up] = new KeyOrMouse(Keys.W); + keyMapping[(int)InputType.Down] = new KeyOrMouse(Keys.S); + keyMapping[(int)InputType.Left] = new KeyOrMouse(Keys.A); + keyMapping[(int)InputType.Right] = new KeyOrMouse(Keys.D); + keyMapping[(int)InputType.Run] = new KeyOrMouse(Keys.LeftShift); + keyMapping[(int)InputType.Chat] = new KeyOrMouse(Keys.Tab); + keyMapping[(int)InputType.Select] = new KeyOrMouse(Keys.E); + + keyMapping[(int)InputType.Use] = new KeyOrMouse(0); + keyMapping[(int)InputType.Aim] = new KeyOrMouse(1); foreach (XElement subElement in doc.Root.Elements()) { @@ -124,19 +152,31 @@ namespace Barotrauma { InputType inputType; Keys key; - if (Enum.TryParse(attribute.Name.ToString(), true, out inputType) && - Enum.TryParse(attribute.Value.ToString(), true, out key)) + int mouseButton; + if (Enum.TryParse(attribute.Name.ToString(), true, out inputType)) { - keyMapping[(int)inputType] = key; + if (Enum.TryParse(attribute.Value.ToString(), true, out key)) + { + keyMapping[(int)inputType] = new KeyOrMouse(key); + } + else if (int.TryParse(attribute.Value.ToString(), out mouseButton)) + { + keyMapping[(int)inputType] = new KeyOrMouse(mouseButton); + } + } } break; } - } + } + + UnsavedSettings = false; } public void Save(string filePath) { + UnsavedSettings = false; + XDocument doc = new XDocument(); if (doc.Root == null) @@ -173,12 +213,12 @@ namespace Barotrauma new XAttribute("path", SelectedContentPackage.Path))); } - doc.Save(filePath); } private bool ChangeSoundVolume(float barScroll) { + UnsavedSettings = true; SoundVolume = MathHelper.Clamp(barScroll, 0.0f, 1.0f); return true; @@ -186,49 +226,152 @@ namespace Barotrauma private bool ChangeMusicVolume(float barScroll) { + UnsavedSettings = true; MusicVolume = MathHelper.Clamp(barScroll, 0.0f, 1.0f); return true; } + private bool ToggleFullScreen(object userData) + { + UnsavedSettings = true; + FullScreenEnabled = !FullScreenEnabled; + return true; + } + + public void ResetSettingsFrame() + { + settingsFrame = null; + } + private void CreateSettingsFrame() { settingsFrame = new GUIFrame(new Rectangle(0, 0, 500, 500), null, Alignment.Center, GUI.Style); - new GUITextBlock(new Rectangle(0, 0, 100, 20), "Sound volume:", GUI.Style, settingsFrame); - GUIScrollBar soundScrollBar = new GUIScrollBar(new Rectangle(0, 20, 150, 20), GUI.Style,0.1f, settingsFrame); + new GUITextBlock(new Rectangle(0,-30,0,30), "Settings",GUI.Style,Alignment.TopCenter, Alignment.TopCenter, settingsFrame, false, GUI.LargeFont); + + int x=0, y = 10; + + new GUITextBlock(new Rectangle(0, y, 20, 20), "Resolution", GUI.Style, Alignment.TopLeft, Alignment.TopLeft, settingsFrame); + var resolutionDD = new GUIDropDown(new Rectangle(0, y + 20, 180, 20), "", GUI.Style, settingsFrame); + + var supportedModes = new List(); + foreach (DisplayMode mode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes) + { + if (supportedModes.FirstOrDefault(m => m.Width == mode.Width && m.Height == mode.Height) != null) continue; + + resolutionDD.AddItem(mode.Width + "x" + mode.Height, mode); + supportedModes.Add(mode); + + if (GraphicsWidth == mode.Width && GraphicsHeight == mode.Height) resolutionDD.SelectItem(mode); + } + + if (resolutionDD.SelectedItemData == null) + { + resolutionDD.SelectItem(GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.Last()); + } + + y += 50; + + var fullScreenTick = new GUITickBox(new Rectangle(x, y, 20, 20), "Fullscreen", Alignment.TopLeft, settingsFrame); + fullScreenTick.OnSelected = ToggleFullScreen; + fullScreenTick.Selected = FullScreenEnabled; + + y += 50; + + new GUITextBlock(new Rectangle(0, y, 100, 20), "Sound volume:", GUI.Style, settingsFrame); + GUIScrollBar soundScrollBar = new GUIScrollBar(new Rectangle(0, y+20, 150, 20), GUI.Style,0.1f, settingsFrame); soundScrollBar.BarScroll = SoundVolume; soundScrollBar.OnMoved = ChangeSoundVolume; - new GUITextBlock(new Rectangle(0, 40, 100, 20), "Music volume:", GUI.Style, settingsFrame); - GUIScrollBar musicScrollBar = new GUIScrollBar(new Rectangle(0, 60, 150, 20), GUI.Style, 0.1f, settingsFrame); + new GUITextBlock(new Rectangle(0, y+40, 100, 20), "Music volume:", GUI.Style, settingsFrame); + GUIScrollBar musicScrollBar = new GUIScrollBar(new Rectangle(0, y+60, 150, 20), GUI.Style, 0.1f, settingsFrame); musicScrollBar.BarScroll = MusicVolume; musicScrollBar.OnMoved = ChangeMusicVolume; - int x = 250; - int y = 60; + x = 200; + y = 10; - new GUITextBlock(new Rectangle(x, 40, 100, 20), "Controls:", GUI.Style, settingsFrame); + new GUITextBlock(new Rectangle(x, y, 20, 20), "Content package", GUI.Style, Alignment.TopLeft, Alignment.TopLeft, settingsFrame); + var contentPackageDD = new GUIDropDown(new Rectangle(x, y + 20, 200, 20), "", GUI.Style, settingsFrame); + + foreach (ContentPackage contentPackage in ContentPackage.list) + { + contentPackageDD.AddItem(contentPackage.Name, contentPackage); + + if (SelectedContentPackage == contentPackage) contentPackageDD.SelectItem(contentPackage); + } + + y += 50; + new GUITextBlock(new Rectangle(x, y, 100, 20), "Controls:", GUI.Style, settingsFrame); + y += 30; var inputNames = Enum.GetNames(typeof(InputType)); for (int i = 0; i< inputNames.Length; i++) { new GUITextBlock(new Rectangle(x, y, 100, 20), inputNames[i]+": ", GUI.Style, settingsFrame); - var keyBox = new GUITextBox(new Rectangle(x + 100, y, 70, 15), GUI.Style, settingsFrame); + var keyBox = new GUITextBox(new Rectangle(x + 100, y, 120, 15), GUI.Style, settingsFrame); keyBox.Text = keyMapping[i].ToString(); - keyBox.OnTextChanged = MapKey; + keyBox.UserData = i; + keyBox.OnSelected += KeyBoxSelected; + keyBox.SelectedColor = Color.Gold * 0.3f; y += 20; } - var applyButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Apply", GUI.Style, settingsFrame); + applyButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Apply", Alignment.BottomRight, GUI.Style, settingsFrame); applyButton.OnClicked = ApplyClicked; } - private bool MapKey(GUITextBox textBox, string text) + private void KeyBoxSelected(GUITextBox textBox, Keys key) { + textBox.Text = ""; + CoroutineManager.StartCoroutine(WaitForKeyPress(textBox)); + } + + private bool MarkUnappliedChanges(GUIButton button, object obj) + { + UnsavedSettings = true; + return true; } + private bool SelectResolution(GUIComponent selected) + { + DisplayMode mode = selected.UserData as DisplayMode; + if (mode == null) return false; + + GraphicsWidth = mode.Width; + GraphicsHeight = mode.Height; + + UnsavedSettings = true; + + return true; + } + + + + private IEnumerable WaitForKeyPress(GUITextBox keyBox) + { + while (keyBox.Selected && PlayerInput.GetKeyboardState.GetPressedKeys().Length==0) + { + if (Screen.Selected != GameMain.MainMenuScreen) yield return CoroutineStatus.Success; + + yield return CoroutineStatus.Running; + } + + UnsavedSettings = true; + + Keys key = PlayerInput.GetKeyboardState.GetPressedKeys()[0]; + + int keyIndex = (int)keyBox.UserData; + keyMapping[keyIndex] = new KeyOrMouse(key); + + keyBox.Text = key.ToString("G"); + keyBox.Deselect(); + + yield return CoroutineStatus.Success; + } + private bool ApplyClicked(GUIButton button, object userData) { Save("config.xml"); diff --git a/Subsurface/Source/Items/Components/Holdable/Holdable.cs b/Subsurface/Source/Items/Components/Holdable/Holdable.cs index 4d4b10d5b..881487f5e 100644 --- a/Subsurface/Source/Items/Components/Holdable/Holdable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Holdable.cs @@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (!attachable || item.body==null) return true; - if (character != null && !character.GetInputState(InputType.SecondaryHeld)) return false; + if (character != null && !character.IsKeyDown(InputType.Aim)) return false; item.Drop(); @@ -238,7 +238,7 @@ namespace Barotrauma.Items.Components //item.sprite.Depth = picker.AnimController.GetLimb(LimbType.RightHand).sprite.Depth + 0.01f; - ac.HoldItem(deltaTime, item, handlePos, holdPos, aimPos, picker.GetInputState(InputType.SecondaryHeld), holdAngle); + ac.HoldItem(deltaTime, item, handlePos, holdPos, aimPos, picker.IsKeyDown(InputType.Aim), holdAngle); } protected void Flip(Item item) diff --git a/Subsurface/Source/Items/Components/Holdable/MeleeWeapon.cs b/Subsurface/Source/Items/Components/Holdable/MeleeWeapon.cs index 4d902b2f5..56d0b7f97 100644 --- a/Subsurface/Source/Items/Components/Holdable/MeleeWeapon.cs +++ b/Subsurface/Source/Items/Components/Holdable/MeleeWeapon.cs @@ -52,7 +52,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (character == null || reloadTimer>0.0f) return false; - if (!character.GetInputState(InputType.SecondaryHeld) || hitting) return false; + if (!character.IsKeyDown(InputType.Aim) || hitting) return false; user = character; @@ -66,7 +66,7 @@ namespace Barotrauma.Items.Components foreach (Limb l in character.AnimController.Limbs) { - item.body.FarseerBody.IgnoreCollisionWith(l.body.FarseerBody); + //item.body.FarseerBody.IgnoreCollisionWith(l.body.FarseerBody); if (character.AnimController.InWater) continue; if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) continue; @@ -107,7 +107,7 @@ namespace Barotrauma.Items.Components reloadTimer -= deltaTime; - if (!picker.GetInputState(InputType.SecondaryHeld) && !hitting) hitPos = 0.0f; + if (!picker.IsKeyDown(InputType.Aim) && !hitting) hitPos = 0.0f; ApplyStatusEffects(ActionType.OnActive, deltaTime, picker); @@ -119,7 +119,7 @@ namespace Barotrauma.Items.Components if (!hitting) { - if (picker.GetInputState(InputType.SecondaryHeld)) + if (picker.IsKeyDown(InputType.Aim)) { hitPos = (float)System.Math.Min(hitPos+deltaTime*5.0f, MathHelper.Pi*0.7f); @@ -167,10 +167,10 @@ namespace Barotrauma.Items.Components item.body.CollisionCategories = Physics.CollisionMisc; item.body.CollidesWith = Physics.CollisionWall; - foreach (Limb l in picker.AnimController.Limbs) - { - item.body.FarseerBody.RestoreCollisionWith(l.body.FarseerBody); - } + //foreach (Limb l in picker.AnimController.Limbs) + //{ + // item.body.FarseerBody.RestoreCollisionWith(l.body.FarseerBody); + //} } diff --git a/Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs b/Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs index 587b9ba94..fbb0f21ff 100644 --- a/Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs +++ b/Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs @@ -53,7 +53,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (character == null) return false; - if (!character.GetInputState(InputType.SecondaryHeld) || reload > 0.0f) return false; + if (!character.IsKeyDown(InputType.Aim) || reload > 0.0f) return false; IsActive = true; reload = 1.0f; diff --git a/Subsurface/Source/Items/Components/Holdable/RepairTool.cs b/Subsurface/Source/Items/Components/Holdable/RepairTool.cs index 3568932b6..e7a9c7e5b 100644 --- a/Subsurface/Source/Items/Components/Holdable/RepairTool.cs +++ b/Subsurface/Source/Items/Components/Holdable/RepairTool.cs @@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (character == null) return false; - if (!character.GetInputState(InputType.SecondaryHeld)) return false; + if (!character.IsKeyDown(InputType.Aim)) return false; //if (DoesUseFail(character)) return false; @@ -161,7 +161,7 @@ namespace Barotrauma.Items.Components } else if ((targetLimb = (targetBody.UserData as Limb)) != null) { - if (character.GetInputState(InputType.SecondaryHeld)) + if (character.IsKeyDown(InputType.Aim)) { targetLimb.character.Health += limbFixAmount * degreeOfSuccess; //isActive = true; diff --git a/Subsurface/Source/Items/Components/Holdable/Throwable.cs b/Subsurface/Source/Items/Components/Holdable/Throwable.cs index 3f5e17178..814fe1c2b 100644 --- a/Subsurface/Source/Items/Components/Holdable/Throwable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Throwable.cs @@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components public override bool Use(float deltaTime, Character character = null) { if (character == null) return false; - if (!character.GetInputState(InputType.SecondaryHeld) || throwing) return false; + if (!character.IsKeyDown(InputType.Aim) || throwing) return false; //Vector2 diff = Vector2.Normalize(character.CursorPosition - character.AnimController.RefLimb.Position); @@ -75,7 +75,7 @@ namespace Barotrauma.Items.Components if (!item.body.Enabled) return; if (!picker.HasSelectedItem(item)) IsActive = false; - if (!picker.GetInputState(InputType.SecondaryHeld) && !throwing) throwPos = 0.0f; + if (!picker.IsKeyDown(InputType.Aim) && !throwing) throwPos = 0.0f; ApplyStatusEffects(ActionType.OnActive, deltaTime, picker); @@ -85,7 +85,7 @@ namespace Barotrauma.Items.Components if (!throwing) { - if (picker.GetInputState(InputType.SecondaryHeld)) + if (picker.IsKeyDown(InputType.Aim)) { throwPos = (float)System.Math.Min(throwPos+deltaTime*5.0f, MathHelper.Pi*0.7f); diff --git a/Subsurface/Source/Items/Components/Machines/Engine.cs b/Subsurface/Source/Items/Components/Machines/Engine.cs index b940fe3e1..031a53101 100644 --- a/Subsurface/Source/Items/Components/Machines/Engine.cs +++ b/Subsurface/Source/Items/Components/Machines/Engine.cs @@ -63,6 +63,8 @@ namespace Barotrauma.Items.Components currPowerConsumption = Math.Abs(targetForce)/100.0f * powerConsumption; + if (powerConsumption == 0.0f) voltage = 1.0f; + Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f); if (Force != 0.0f) { diff --git a/Subsurface/Source/Items/Components/Projectile.cs b/Subsurface/Source/Items/Components/Projectile.cs index aeb462c97..68057f21e 100644 --- a/Subsurface/Source/Items/Components/Projectile.cs +++ b/Subsurface/Source/Items/Components/Projectile.cs @@ -109,7 +109,7 @@ namespace Barotrauma.Items.Components { item.body.FarseerBody.RestoreCollisionWith(stickTarget); } - catch + catch (Exception e) { #if DEBUG DebugConsole.ThrowError("Failed to restore collision with stickTarget", e); diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index 31470ffe3..1fd38a981 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components if (character != Character.Controlled || character != user) return; if (Screen.Selected != GameMain.EditMapScreen && - character.GetInputState(InputType.Select) && + character.IsKeyHit(InputType.Select) && character.SelectedConstruction==this.item) character.SelectedConstruction = null; Connection.DrawConnections(spriteBatch, this, character); diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index d9696d903..2c1d5fcfb 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -727,7 +727,7 @@ namespace Barotrauma namesBox.Text = relatedItem.JoinedNames; namesBox.UserData = new ObjectProperty(property, relatedItem); - namesBox.OnEnter = EnterProperty; + namesBox.OnEnterPressed = EnterProperty; namesBox.OnTextChanged = PropertyChanged; y += 30; @@ -754,7 +754,7 @@ namespace Barotrauma } propertyBox.UserData = objectProperty; - propertyBox.OnEnter = EnterProperty; + propertyBox.OnEnterPressed = EnterProperty; propertyBox.OnTextChanged = PropertyChanged; y = y + height+10; } @@ -879,8 +879,8 @@ namespace Barotrauma bool pickHit = false, selectHit = false; if (Screen.Selected == GameMain.EditMapScreen) { - pickHit = picker.GetInputState(InputType.Select); - selectHit = picker.GetInputState(InputType.Select); + pickHit = picker.IsKeyHit(InputType.Select); + selectHit = picker.IsKeyHit(InputType.Select); } else { @@ -891,13 +891,13 @@ namespace Barotrauma } else if (forceActionKey) { - if (ic.PickKey == InputType.ActionHit) pickHit = true; - if (ic.SelectKey == InputType.ActionHit) selectHit = true; + if (ic.PickKey == InputType.Use) pickHit = true; + if (ic.SelectKey == InputType.Use) selectHit = true; } else { - pickHit = picker.GetInputState(ic.PickKey); - selectHit = picker.GetInputState(ic.SelectKey); + pickHit = picker.IsKeyHit(ic.PickKey); + selectHit = picker.IsKeyHit(ic.SelectKey); } } @@ -925,7 +925,7 @@ namespace Barotrauma if (picker.SelectedConstruction == this) { - if (picker.GetInputState(InputType.Select)) picker.SelectedConstruction = null; + if (picker.IsKeyHit(InputType.Select)) picker.SelectedConstruction = null; } else if (selected) { diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index c63a00e2a..6ed255969 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -179,7 +179,7 @@ namespace Barotrauma new GUITextBlock(new Rectangle(0, y, 100, 20), "ID Card tags:", Color.Transparent, Color.Black, Alignment.TopLeft, null, editingHUD); GUITextBox propertyBox = new GUITextBox(new Rectangle(100, y, 200, 20), GUI.Style, editingHUD); propertyBox.Text = string.Join(", ", idCardTags); - propertyBox.OnEnter = EnterIDCardTags; + propertyBox.OnEnterPressed = EnterIDCardTags; propertyBox.OnTextChanged = TextBoxChanged; y = y + 30; @@ -187,7 +187,7 @@ namespace Barotrauma propertyBox = new GUITextBox(new Rectangle(100, y, 200, 20), GUI.Style, editingHUD); propertyBox.Text = (assignedJob == null) ? "None" : assignedJob.Name; - propertyBox.OnEnter = EnterAssignedJob; + propertyBox.OnEnterPressed = EnterAssignedJob; propertyBox.OnTextChanged = TextBoxChanged; y = y + 30; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index f44d800a6..a93011746 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -9,10 +9,8 @@ using Barotrauma.Networking.ReliableMessages; namespace Barotrauma.Networking { - class GameServer : NetworkMember + partial class GameServer : NetworkMember { - public bool ShowNetStats; - public List connectedClients = new List(); //for keeping track of disconnected clients in case the reconnect shortly after @@ -24,41 +22,13 @@ namespace Barotrauma.Networking bool started; private NetServer server; - private NetPeerConfiguration config; - - private TimeSpan sparseUpdateInterval = new TimeSpan(0, 0, 0, 3); - private DateTime sparseUpdateTimer; + private NetPeerConfiguration config; - private TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 40); + private DateTime sparseUpdateTimer; private DateTime refreshMasterTimer; - private BanList banList; - private bool masterServerResponded; - private bool registeredToMaster; - - private string password; - - private bool autoRestart; - - public bool AutoRestart - { - get { return (connectedClients.Count==0) ? false : autoRestart; } - set - { - autoRestart = value; - - AutoRestartTimer = autoRestart ? 20.0f : 0.0f; - } - } - public float AutoRestartTimer; - - public BanList BanList - { - get { return banList; } - } - public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10) { var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 170, 20, 150, 25), "End round", Alignment.TopLeft, GUI.Style, inGameHUD); @@ -238,6 +208,7 @@ namespace Barotrauma.Networking public override void Update(float deltaTime) { if (ShowNetStats) netStats.Update(deltaTime); + if (settingsFrame != null) settingsFrame.Update(deltaTime); if (!started) return; @@ -1033,6 +1004,8 @@ namespace Barotrauma.Networking public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) { base.Draw(spriteBatch); + + if (settingsFrame != null) settingsFrame.Draw(spriteBatch); if (!ShowNetStats) return; @@ -1292,16 +1265,6 @@ namespace Barotrauma.Networking return preferredClient; } - - private byte PlayerCountToByte(int playerCount, int maxPlayers) - { - byte byteVal = (byte)playerCount; - - byteVal |= (byte)((maxPlayers-1) << 4); - - return byteVal; - } - /// /// sends some random data to the clients /// use for debugging purposes diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index b16864f04..af75c2428 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -109,7 +109,7 @@ namespace Barotrauma.Networking new Rectangle(chatBox.Rect.X, chatBox.Rect.Y + chatBox.Rect.Height + 20, chatBox.Rect.Width, 25), Color.White * 0.5f, Color.Black, Alignment.TopLeft, Alignment.Left, GUI.Style, inGameHUD); chatMsgBox.Font = GUI.SmallFont; - chatMsgBox.OnEnter = EnterChatMessage; + chatMsgBox.OnEnterPressed = EnterChatMessage; crewButton = new GUIButton(new Rectangle(chatBox.Rect.Right-80, chatBox.Rect.Y-30, 80, 20), "Crew", GUI.Style, inGameHUD); crewButton.OnClicked = ToggleCrewFrame; @@ -175,17 +175,6 @@ namespace Barotrauma.Networking return true; } - //protected void UpdateCrewFrame(List connectedClients) - //{ - // List characterList = new List(); - // foreach (Client c in connectedClients) - // { - // if (c.character != null && c.inGame) characterList.Add(c.character); - // } - - // CreateCrewFrame(characterList); - //} - public bool EnterChatMessage(GUITextBox textBox, string message) { if (string.IsNullOrWhiteSpace(message)) return false; @@ -268,6 +257,15 @@ namespace Barotrauma.Networking public virtual void Disconnect() { } + protected byte PlayerCountToByte(int playerCount, int maxPlayers) + { + byte byteVal = (byte)playerCount; + + byteVal |= (byte)((maxPlayers - 1) << 4); + + return byteVal; + } + public static int ByteToPlayerCount(byte byteVal, out int maxPlayers) { maxPlayers = (byteVal >> 4)+1; diff --git a/Subsurface/Source/PlayerInput.cs b/Subsurface/Source/PlayerInput.cs index 48cf8a746..836c3b5c3 100644 --- a/Subsurface/Source/PlayerInput.cs +++ b/Subsurface/Source/PlayerInput.cs @@ -7,62 +7,177 @@ namespace Barotrauma public enum InputType { Select, - ActionHit, ActionHeld, - SecondaryHit, SecondaryHeld, - Left, Right, Up, Down, + Use, + Aim, + Up, Down, Left, Right, Run, Chat } + public class KeyOrMouse + { + Keys keyBinding; + int? mouseButton; + + public KeyOrMouse(Keys keyBinding) + { + this.keyBinding = keyBinding; + } + + public KeyOrMouse(int mouseButton) + { + this.mouseButton = mouseButton; + } + + public bool IsDown() + { + if (mouseButton==null) + { + return PlayerInput.KeyDown(keyBinding); + } + else if (mouseButton == 0) + { + return PlayerInput.LeftButtonDown(); + } + else if (mouseButton == 1) + { + return PlayerInput.RightButtonDown(); + } + + return false; + } + + public bool IsHit() + { + if (mouseButton == null) + { + return PlayerInput.KeyHit(keyBinding); + } + else if (mouseButton == 0) + { + return PlayerInput.LeftButtonClicked(); + } + else if (mouseButton == 1) + { + return PlayerInput.RightButtonClicked(); + } + + return false; + } + + public override string ToString() + { + if (mouseButton==null) + { + return keyBinding.ToString(); + } + else if (mouseButton==0) + { + return "Mouse1"; + } + else if (mouseButton==1) + { + return "Mouse2"; + } + + return "None"; + } + } + class Key { - private bool state, stateQueue; - private bool canBeHeld; + private bool hit, hitQueue; + private bool held, heldQueue; - public bool CanBeHeld - { - get { return canBeHeld; } - } + + KeyOrMouse binding; + + //public bool CanBeHeld + //{ + // get { return canBeHeld; } + //} - public Key(bool canBeHeld) + public Key(KeyOrMouse binding) { - this.canBeHeld = canBeHeld; + this.binding = binding; } - public bool State + public bool Hit { get { - return state; + return hit; } set { - //if (value == false) return; - state = value; - //if (value) stateQueue = value; + hit = value; } } - public void SetState(bool value) + public bool Held + { + get + { + return held; + } + set + { + held = value; + } + } + + public KeyOrMouse State + { + get { return binding; } + } + + public void SetState() { - state = value; - if (value) stateQueue = value; + hit = binding.IsHit(); + if (hit) hitQueue = true; + + held = binding.IsDown(); + if (held) heldQueue = true; } public bool Dequeue { get { - bool value = stateQueue; - stateQueue = false; + bool value = hitQueue; + hitQueue = false; return value; } } - public void Reset() + public bool DequeueHeld + { + get + { + bool value = heldQueue; + heldQueue = false; + return value; + } + } + + + public void Reset() + { + hit = false; + held = false; + } + + public void ResetHit() { - if (!canBeHeld) state = false; + hit = false; //stateQueue = false; } + + + public void ResetHeld() + { + held = false; + //stateQueue = false; + } } public static class PlayerInput diff --git a/Subsurface/Source/Screens/MainMenuScreen.cs b/Subsurface/Source/Screens/MainMenuScreen.cs index e57183348..aa2af5025 100644 --- a/Subsurface/Source/Screens/MainMenuScreen.cs +++ b/Subsurface/Source/Screens/MainMenuScreen.cs @@ -199,6 +199,23 @@ namespace Barotrauma public void SelectTab(Tab tab) { + int oldTab = selectedTab; + + + if (GameMain.Config.UnsavedSettings) + { + var applyBox = new GUIMessageBox("Apply changes?", "Do you want to apply the settings or discard the changes?", + new string[] {"Apply", "Discard"}); + applyBox.Buttons[0].OnClicked += applyBox.Close; + applyBox.Buttons[0].OnClicked += ApplySettings; + applyBox.Buttons[0].UserData = tab; + applyBox.Buttons[1].OnClicked += applyBox.Close; + applyBox.Buttons[1].OnClicked += DiscardSettings; + applyBox.Buttons[1].UserData = tab; + + return; + } + selectedTab = (int)tab; switch (selectedTab) @@ -207,11 +224,29 @@ namespace Barotrauma UpdateLoadScreen(); break; case (int)Tab.Settings: + GameMain.Config.ResetSettingsFrame(); menuTabs[(int)Tab.Settings] = GameMain.Config.SettingsFrame; break; } } + private bool ApplySettings(GUIButton button, object obj) + { + GameMain.Config.Save("config.xml"); + selectedTab = (int)obj; + + return true; + } + + private bool DiscardSettings(GUIButton button, object obj) + { + GameMain.Config.Load("config.xml"); + selectedTab = (int)obj; + + return true; + } + + private bool TutorialButtonClicked(GUIButton button, object obj) { TutorialMode.StartTutorial(); diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 203bc46d2..aa7ac5d7a 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -119,7 +119,7 @@ namespace Barotrauma //server info panel ------------------------------------------------------------ infoFrame = new GUIFrame(new Rectangle(0, 0, (int)(panelRect.Width * 0.7f), (int)(panelRect.Height * 0.6f)), GUI.Style, menu); - //infoFrame.Padding = GUI.style.smallPadding; + infoFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f); ; //chatbox ---------------------------------------------------------------------- GUIFrame chatFrame = new GUIFrame( @@ -127,11 +127,12 @@ namespace Barotrauma (int)(panelRect.Width * 0.7f), (int)(panelRect.Height * 0.4f - 20)), GUI.Style, menu); + chatFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 40.0f); chatBox = new GUIListBox(new Rectangle(0,0,0,chatFrame.Rect.Height-80), Color.White, GUI.Style, chatFrame); textBox = new GUITextBox(new Rectangle(0, 25, 0, 25), Alignment.Bottom, GUI.Style, chatFrame); textBox.Font = GUI.SmallFont; - textBox.OnEnter = EnterChatMessage; + textBox.OnEnterPressed = EnterChatMessage; //player info panel ------------------------------------------------------------ @@ -148,6 +149,8 @@ namespace Barotrauma (int)(panelRect.Width * 0.3f - 20), (int)(panelRect.Height * 0.4f - 20)), GUI.Style, menu); + playerListFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 40.0f); + playerList = new GUIListBox(new Rectangle(0,0,0,0), null, GUI.Style, playerListFrame); playerList.OnSelected = SelectPlayer; @@ -278,10 +281,14 @@ namespace Barotrauma if (IsServer && GameMain.Server != null) { - GUIButton startButton = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", Alignment.BottomRight, GUI.Style, infoFrame); + GUIButton startButton = new GUIButton(new Rectangle(0, 0, 80, 30), "Start", Alignment.BottomRight, GUI.Style, infoFrame); startButton.OnClicked = GameMain.Server.StartGameClicked; startButton.UserData = "startButton"; + GUIButton settingsButton = new GUIButton(new Rectangle(-100, 0, 80, 30), "Settings", Alignment.BottomRight, GUI.Style, infoFrame); + settingsButton.OnClicked = GameMain.Server.ToggleSettingsFrame; + settingsButton.UserData = "settingsButton"; + var banListButton = new GUIButton(new Rectangle(0, 30, 100, 20), "Banned IPs", Alignment.BottomRight, GUI.Style, playerList.Parent); banListButton.OnClicked = GameMain.Server.BanList.ToggleBanFrame; banListButton.UserData = "banListButton"; @@ -299,6 +306,10 @@ namespace Barotrauma playYourself.OnSelected = TogglePlayYourself; playYourself.UserData = "playyourself"; } + + if (GameMain.Server.RandomizeSeed) seedBox.Text = ToolBox.RandomSeed(8); + if (GameMain.Server.SubSelectionMode == SelectionMode.Random) subList.Select(Rand.Range(0,subList.CountChildren)); + if (GameMain.Server.ModeSelectionMode == SelectionMode.Random) modeList.Select(Rand.Range(0, modeList.CountChildren)); } else { @@ -322,12 +333,18 @@ namespace Barotrauma playYourself.UserData = "playyourself"; } - new GUITextBlock(new Rectangle(60, 30, 200, 30), "Name: ", GUI.Style, myPlayerFrame); + new GUITextBlock(new Rectangle(100, 30, 200, 30), "Name: ", GUI.Style, myPlayerFrame); - GUITextBox playerName = new GUITextBox(new Rectangle(60, 55, 0, 20), - Alignment.TopLeft, GUI.Style, myPlayerFrame); + GUITextBox playerName = new GUITextBox(new Rectangle(100, 55, 0, 20), Alignment.TopLeft, GUI.Style, myPlayerFrame); playerName.Text = characterInfo.Name; - playerName.OnEnter += ChangeCharacterName; + playerName.OnEnterPressed += ChangeCharacterName; + + GUIButton toggleHead = new GUIButton(new Rectangle(00, 50, 20, 20), "<", GUI.Style, myPlayerFrame); + toggleHead.UserData = -1; + toggleHead.OnClicked = ToggleHead; + toggleHead = new GUIButton(new Rectangle(40, 50, 20, 20), ">", GUI.Style, myPlayerFrame); + toggleHead.UserData = 1; + toggleHead.OnClicked = ToggleHead; new GUITextBlock(new Rectangle(0, 100, 200, 30), "Gender: ", GUI.Style, myPlayerFrame); @@ -374,9 +391,8 @@ namespace Barotrauma } } - private bool TogglePlayYourself(object obj) + private bool TogglePlayYourself(GUITickBox tickBox) { - GUITickBox tickBox = obj as GUITickBox; if (tickBox.Selected) { GameMain.Server.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, GameMain.Server.Name); @@ -398,16 +414,13 @@ namespace Barotrauma return false; } - private bool ToggleAutoRestart(object obj) + private bool ToggleAutoRestart(GUITickBox tickBox) { if (GameMain.Server == null) return false; - GUITickBox tickBox = obj as GUITickBox; - if (tickBox==null) return false; - GameMain.Server.AutoRestart = tickBox.Selected; - GameMain.Server.UpdateNetLobby(obj); + GameMain.Server.UpdateNetLobby(tickBox); return true; } @@ -622,10 +635,23 @@ namespace Barotrauma GUIComponent existing = myPlayerFrame.FindChild("playerhead"); if (existing != null) myPlayerFrame.RemoveChild(existing); - GUIImage image = new GUIImage(new Rectangle(0, 40, 30, 30), characterInfo.HeadSprite, Alignment.TopLeft, myPlayerFrame); + GUIImage image = new GUIImage(new Rectangle(20, 40, 30, 30), characterInfo.HeadSprite, Alignment.TopLeft, myPlayerFrame); image.UserData = "playerhead"; } + private bool ToggleHead(GUIButton button, object userData) + { + int dir = (int)userData; + + if (GameMain.NetworkMember.CharacterInfo == null) return true; + + GameMain.NetworkMember.CharacterInfo.HeadSpriteId += dir; + + UpdatePreviewPlayer(GameMain.NetworkMember.CharacterInfo); + + return true; + } + private bool SwitchGender(GUIButton button, object obj) { Gender gender = (Gender)obj; diff --git a/Subsurface/Source/Screens/NetLobbySettings.cs b/Subsurface/Source/Screens/NetLobbySettings.cs new file mode 100644 index 000000000..85b954910 --- /dev/null +++ b/Subsurface/Source/Screens/NetLobbySettings.cs @@ -0,0 +1,144 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Barotrauma.Networking +{ + enum SelectionMode + { + Manual = 0, Random = 1, Vote = 2 + } + + partial class GameServer : NetworkMember + { + public bool ShowNetStats; + + private TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 40); + private TimeSpan sparseUpdateInterval = new TimeSpan(0, 0, 0, 3); + + private SelectionMode subSelectionMode, modeSelectionMode; + + private bool randomizeSeed; + + private bool registeredToMaster; + + private BanList banList; + + private string password; + + private GUIFrame settingsFrame; + + public float AutoRestartTimer; + + private bool autoRestart; + + public bool AutoRestart + { + get { return (connectedClients.Count == 0) ? false : autoRestart; } + set + { + autoRestart = value; + + AutoRestartTimer = autoRestart ? 20.0f : 0.0f; + } + } + + public SelectionMode SubSelectionMode + { + get { return subSelectionMode; } + } + + public SelectionMode ModeSelectionMode + { + get { return modeSelectionMode; } + } + + public bool RandomizeSeed + { + get { return randomizeSeed; } + } + + public BanList BanList + { + get { return banList; } + } + + private void CreateSettingsFrame() + { + settingsFrame = new GUIFrame(new Rectangle(0,0,GameMain.GraphicsWidth,GameMain.GraphicsHeight), Color.Black*0.5f); + + GUIFrame innerFrame = new GUIFrame(new Rectangle(0,0,400,400), null, Alignment.Center, GUI.Style, settingsFrame); + + var randomizeLevelBox = new GUITickBox(new Rectangle(0, 0, 20, 20), "Randomize level seed", Alignment.Left, innerFrame); + randomizeLevelBox.OnSelected = ToggleRandomizeSeed; + + new GUITextBlock(new Rectangle(0, 35, 100, 20), "Submarine selection:", GUI.Style, innerFrame); + var selectionFrame = new GUIFrame(new Rectangle(0, 60, 300, 20), null, innerFrame); + for (int i = 0; i<3; i++) + { + var selectionTick = new GUITickBox(new Rectangle(i * 100, 00, 20, 20), ((SelectionMode)i).ToString(), Alignment.Left, selectionFrame); + selectionTick.Selected = i == (int)subSelectionMode; + selectionTick.OnSelected = SwitchSubSelection; + selectionTick.UserData = (SelectionMode)i; + } + + new GUITextBlock(new Rectangle(0, 85, 100, 20), "Mode selection:", GUI.Style, innerFrame); + selectionFrame = new GUIFrame(new Rectangle(0, 110, 300, 20), null, innerFrame); + for (int i = 0; i<3; i++) + { + var selectionTick = new GUITickBox(new Rectangle(i*100, 0, 20, 20), ((SelectionMode)i).ToString(), Alignment.Left, selectionFrame); + selectionTick.Selected = i == (int)modeSelectionMode; + selectionTick.OnSelected = SwitchModeSelection; + selectionTick.UserData = (SelectionMode)i; + } + } + + private bool SwitchSubSelection(GUITickBox tickBox) + { + subSelectionMode = (SelectionMode)tickBox.UserData; + + foreach (GUIComponent otherTickBox in tickBox.Parent.children) + { + if (otherTickBox == tickBox) continue; + ((GUITickBox)otherTickBox).Selected = false; + } + + return true; + } + + private bool SwitchModeSelection(GUITickBox tickBox) + { + modeSelectionMode = (SelectionMode)tickBox.UserData; + + foreach (GUIComponent otherTickBox in tickBox.Parent.children) + { + if (otherTickBox == tickBox) continue; + ((GUITickBox)otherTickBox).Selected = false; + } + + return true; + } + + private bool ToggleRandomizeSeed(GUITickBox tickBox) + { + randomizeSeed = tickBox.Selected; + return true; + } + + public bool ToggleSettingsFrame(GUIButton button, object obj) + { + if (settingsFrame==null) + { + CreateSettingsFrame(); + } + else + { + settingsFrame = null; + } + + return true; + } + } +} diff --git a/Subsurface/Source/Screens/SettingsScreen.cs b/Subsurface/Source/Screens/SettingsScreen.cs deleted file mode 100644 index f905e8e17..000000000 --- a/Subsurface/Source/Screens/SettingsScreen.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Barotrauma -{ - class SettingsScreen : Screen - { - } -} diff --git a/Subsurface/Source/Utils/TextureLoader.cs b/Subsurface/Source/Utils/TextureLoader.cs index 79b0f17a7..1081ccad5 100644 --- a/Subsurface/Source/Utils/TextureLoader.cs +++ b/Subsurface/Source/Utils/TextureLoader.cs @@ -46,18 +46,13 @@ namespace Barotrauma { try { -#if WINDOWS - using (Stream fileStream = File.OpenRead(path)) - return FromStream(fileStream, preMultiplyAlpha); -#endif -#if LINUX + using (Stream fileStream = File.OpenRead(path)) { var texture = Texture2D.FromStream(_graphicsDevice, fileStream); texture = PreMultiplyAlpha(texture); return texture; } -#endif } catch (Exception e) @@ -68,36 +63,6 @@ namespace Barotrauma } -#if WINDOWS - private static Texture2D FromStream(Stream stream, bool preMultiplyAlpha = true) - { - Texture2D texture; - - if (_needsBmp) - { - // Load image using GDI because Texture2D.FromStream doesn't support BMP - using (Image image = Image.FromStream(stream)) - { - // Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally - using (MemoryStream ms = new MemoryStream()) - { - image.Save(ms, ImageFormat.Png); - ms.Seek(0, SeekOrigin.Begin); - texture = Texture2D.FromStream(_graphicsDevice, ms); - } - } - } - else - { - texture = Texture2D.FromStream(_graphicsDevice, stream); - } - - if (preMultiplyAlpha) texture = PreMultiplyAlpha(texture); - - return texture; - } -#endif - private static Texture2D PreMultiplyAlpha(Texture2D texture) { // Setup a render target to hold our final texture which will have premulitplied alpha values diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 7b9744537cafc79660cfb5df8e3ab0d1a14c2a3e..5dd7e7f11fe3dcb46a7f41ee92b1afd0602ce7d0 100644 GIT binary patch delta 30629 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 Z4wpwf|GYNVv0wCT{b$1Acfy-9{uiaYYrOyf