From 8e491ae8557bbb0f718704f7d413f6d954ba0294 Mon Sep 17 00:00:00 2001 From: Regalis Date: Fri, 15 Jan 2016 23:04:34 +0200 Subject: [PATCH] Bunch of editor improvements/bugfixes: free node positioning when placing wires, options to hide hulls, gaps & links, hidden entities can't be selected, more accurate stair selecting, disabled UImessages, camera position fixes --- Subsurface/Source/Items/Components/Door.cs | 16 ++++- .../Source/Items/Components/Signal/Wire.cs | 57 +++++++++++------- Subsurface/Source/Items/Item.cs | 15 +++-- Subsurface/Source/Items/ItemPrefab.cs | 2 +- Subsurface/Source/Map/Gap.cs | 15 +++-- Subsurface/Source/Map/Hull.cs | 10 ++- Subsurface/Source/Map/MapEntity.cs | 16 ++++- Subsurface/Source/Map/MapEntityPrefab.cs | 9 ++- Subsurface/Source/Map/Structure.cs | 21 +++++++ Subsurface/Source/Map/WayPoint.cs | 36 ++++++++--- Subsurface/Source/Screens/EditMapScreen.cs | 46 +++++++++----- Subsurface/Source/Utils/MathUtils.cs | 6 ++ Subsurface_Solution.v12.suo | Bin 817152 -> 842240 bytes 13 files changed, 184 insertions(+), 65 deletions(-) diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index d26bbf258..356b1ad31 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -55,7 +55,19 @@ namespace Barotrauma.Items.Components linkedGap = e as Gap; if (linkedGap != null) return linkedGap; } - linkedGap = new Gap(item.Rect, Item.Submarine); + Rectangle rect = item.Rect; + if (isHorizontal) + { + rect.Y += 5; + rect.Height += 10; + } + else + { + rect.X -= 5; + rect.Width += 10; + } + + linkedGap = new Gap(rect, Item.Submarine); linkedGap.Submarine = item.Submarine; linkedGap.Open = openState; item.linkedTo.Add(linkedGap); @@ -341,7 +353,7 @@ namespace Barotrauma.Items.Components doorSprite.Remove(); - convexHull.Remove(); + if (convexHull!=null) convexHull.Remove(); if (convexHull2 != null) convexHull2.Remove(); } diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index 776dee504..ed3bfb30f 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -173,18 +173,19 @@ namespace Barotrauma.Items.Components item.FindHull(); - Vector2 position = item.Position; - position.X = MathUtils.Round(item.Position.X, nodeDistance); - if (item.CurrentHull == null) - { - position.Y = MathUtils.Round(item.Position.Y, nodeDistance); - } - else - { - position.Y -= item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height; - position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor); - position.Y += item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height; - } + //Vector2 position = item.Position; + + //position.X = MathUtils.Round(item.Position.X, nodeDistance); + //if (item.CurrentHull == null) + //{ + // position.Y = MathUtils.Round(item.Position.Y, nodeDistance); + //} + //else + //{ + // position.Y -= item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height; + // position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor); + // position.Y += item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height; + //} newNodePos = RoundNode(item.Position, item.CurrentHull)-Submarine.HiddenSubPosition; @@ -249,16 +250,26 @@ namespace Barotrauma.Items.Components private Vector2 RoundNode(Vector2 position, Hull hull) { - position.X = MathUtils.Round(position.X, nodeDistance); - if (hull == null) + if (Screen.Selected == GameMain.EditMapScreen) { - position.Y = MathUtils.Round(position.Y, nodeDistance); + //position = GameMain.EditMapScreen.Cam.ScreenToWorld(PlayerInput.MousePosition) - Submarine.Loaded.Position;// Nodes[(int)selectedNodeIndex]; + + position.X = MathUtils.Round(position.X, Submarine.GridSize.X / 2.0f); + position.Y = MathUtils.Round(position.Y, Submarine.GridSize.Y / 2.0f); } else { - position.Y -= hull.Rect.Y - hull.Rect.Height; - position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor); - position.Y += hull.Rect.Y -hull.Rect.Height; + position.X = MathUtils.Round(position.X, nodeDistance); + if (hull == null) + { + position.Y = MathUtils.Round(position.Y, nodeDistance); + } + else + { + position.Y -= hull.Rect.Y - hull.Rect.Height; + position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor); + position.Y += hull.Rect.Y -hull.Rect.Height; + } } return position; @@ -322,11 +333,13 @@ namespace Barotrauma.Items.Components for (int i = 0; i < Nodes.Count; i++) { Vector2 worldPos = Nodes[i]; - if (item.Submarine != null) worldPos += item.Submarine.Position + Submarine.HiddenSubPosition; + if (Submarine.Loaded != null) worldPos += Submarine.Loaded.Position + Submarine.HiddenSubPosition; worldPos.Y = -worldPos.Y; GUI.DrawRectangle(spriteBatch, worldPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.0f); + if (IsActive) continue; + if (GUIComponent.MouseOn != null || Vector2.Distance(GameMain.EditMapScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), new Vector2(worldPos.X, -worldPos.Y)) > 10.0f) { @@ -382,10 +395,10 @@ namespace Barotrauma.Items.Components private void DrawSection(SpriteBatch spriteBatch, Vector2 start, Vector2 end, Color color) { - if (item.Submarine!=null) + if (Submarine.Loaded!=null) { - start += item.Submarine.DrawPosition+Submarine.HiddenSubPosition; - end += item.Submarine.DrawPosition+Submarine.HiddenSubPosition; + start += Submarine.Loaded.DrawPosition + Submarine.HiddenSubPosition; + end += Submarine.Loaded.DrawPosition + Submarine.HiddenSubPosition; } start.Y = -start.Y; diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 08623c56e..626a4274b 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -29,6 +29,8 @@ namespace Barotrauma public static ItemSpawner Spawner = new ItemSpawner(); public static ItemRemover Remover = new ItemRemover(); + public static bool ShowLinks = true; + private List tags; public Hull CurrentHull; @@ -698,6 +700,8 @@ namespace Barotrauma Color.Green); } + if (!ShowLinks) return; + foreach (MapEntity e in linkedTo) { GUI.DrawLine(spriteBatch, @@ -727,7 +731,7 @@ namespace Barotrauma { if (entity == this || !entity.IsHighlighted) continue; if (linkedTo.Contains(entity)) continue; - if (!entity.Contains(position)) continue; + if (!entity.IsMouseOn(position)) continue; linkedTo.Add(entity); if (entity.IsLinkable && entity.linkedTo != null) entity.linkedTo.Add(this); @@ -790,7 +794,7 @@ namespace Barotrauma foreach (var objectProperty in editableProperties) { - new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, null, editingHUD); + new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, GUI.Style, editingHUD); int height = 20; var editable = objectProperty.Attributes.OfType().FirstOrDefault(); @@ -905,11 +909,9 @@ namespace Barotrauma transformedTrigger.Y - transformedTrigger.Height / 2.0f); dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPos.X), Math.Abs(triggerCenter.Y-displayPos.Y)); - dist = ConvertUnits.ToSimUnits(dist); if (dist > closestDist && closest!=null) continue; dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y)); - dist = ConvertUnits.ToSimUnits(dist); if (closest == null || dist < closestDist) { closest = item; @@ -993,7 +995,8 @@ namespace Barotrauma if (tempRequiredSkill != null) requiredSkill = tempRequiredSkill; - if (!ignoreRequiredItems && !ic.HasRequiredItems(picker, picker == Character.Controlled)) continue; + bool showUiMsg = picker == Character.Controlled && Screen.Selected != GameMain.EditMapScreen; + if (!ignoreRequiredItems && !ic.HasRequiredItems(picker, showUiMsg)) continue; if ((ic.CanBePicked && pickHit && ic.Pick(picker)) || (ic.CanBeSelected && selectHit && ic.Select(picker))) { @@ -1133,7 +1136,7 @@ namespace Barotrauma private bool EnterProperty(GUITextBox textBox, string text) { - textBox.Color = Color.White; + textBox.Color = Color.DarkGreen; var objectProperty = textBox.UserData as ObjectProperty; if (objectProperty == null) return false; diff --git a/Subsurface/Source/Items/ItemPrefab.cs b/Subsurface/Source/Items/ItemPrefab.cs index 222f7d7f7..bd32f8fbc 100644 --- a/Subsurface/Source/Items/ItemPrefab.cs +++ b/Subsurface/Source/Items/ItemPrefab.cs @@ -97,7 +97,7 @@ namespace Barotrauma var item = new Item(new Rectangle((int)position.X, (int)position.Y, (int)sprite.size.X, (int)sprite.size.Y), this, Submarine.Loaded); //constructor.Invoke(lobject); item.Submarine = Submarine.Loaded; - item.SetTransform(ConvertUnits.ToSimUnits(item.Position - Submarine.Loaded.Position), 0.0f); + item.SetTransform(ConvertUnits.ToSimUnits(Submarine.Loaded==null ? item.Position : item.Position - Submarine.Loaded.Position), 0.0f); item.FindHull(); placePosition = Vector2.Zero; diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 3597a90fe..a40fb0849 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -13,6 +13,8 @@ namespace Barotrauma { public static List GapList = new List(); + public static bool ShowGaps = true; + public bool isHorizontal; //private Sound waterSound; @@ -65,6 +67,10 @@ namespace Barotrauma } } + public Gap(Rectangle rectangle) + : this (rectangle, Submarine.Loaded) + { } + public Gap(Rectangle newRect, Submarine submarine) : this(newRect, newRect.Width < newRect.Height, submarine) { } @@ -102,9 +108,9 @@ namespace Barotrauma } } - public override bool Contains(Vector2 position) + public override bool IsMouseOn(Vector2 position) { - return (Submarine.RectContains(WorldRect, position) && + return (ShowGaps && Submarine.RectContains(WorldRect, position) && !Submarine.RectContains(MathUtils.ExpandRect(WorldRect, -5), position)); } @@ -162,10 +168,11 @@ namespace Barotrauma GUI.DrawLine(sb, center + Vector2.One * 5.0f, center + lerpedFlowForce / 10.0f + Vector2.One * 5.0f, Color.Orange); } - - if (!editing) return; + + if (!editing || !ShowGaps) return; Color clr = (open == 0.0f) ? Color.Red : Color.Cyan; + if (isHighlighted) clr = Color.Gold; GUI.DrawRectangle(sb, new Rectangle(WorldRect.X, -WorldRect.Y, rect.Width, rect.Height), clr * 0.5f, true); diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 1382093c7..91dac09e2 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -16,6 +16,8 @@ namespace Barotrauma public static List hullList = new List(); private static EntityGrid entityGrid; + public static bool ShowHulls = true; + public static bool EditWater, EditFire; public static WaterRenderer renderer; @@ -186,8 +188,10 @@ namespace Barotrauma } } - public override bool Contains(Vector2 position) + public override bool IsMouseOn(Vector2 position) { + if (!GameMain.DebugDraw && !ShowHulls) return false; + return (Submarine.RectContains(WorldRect, position) && !Submarine.RectContains(MathUtils.ExpandRect(WorldRect, -8), position)); } @@ -228,7 +232,7 @@ namespace Barotrauma //renderer.Dispose(); - entityGrid.RemoveEntity(this); + if (entityGrid!=null) entityGrid.RemoveEntity(this); hullList.Remove(this); } @@ -371,6 +375,8 @@ namespace Barotrauma public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { + if (!ShowHulls && !GameMain.DebugDraw) return; + if (!editing && !GameMain.DebugDraw) return; Rectangle drawRect = diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 7a6fc88ca..d3a542d81 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -152,7 +152,7 @@ namespace Barotrauma rect.Y += (int)amount.Y; } - public virtual bool Contains(Vector2 position) + public virtual bool IsMouseOn(Vector2 position) { return (Submarine.RectContains(WorldRect, position)); } @@ -268,7 +268,7 @@ namespace Barotrauma if (highLightedEntity == null || e.Sprite == null || (highLightedEntity.Sprite!=null && e.Sprite.Depth < highLightedEntity.Sprite.Depth)) { - if (e.Contains(position)) highLightedEntity = e; + if (e.IsMouseOn(position)) highLightedEntity = e; } e.isSelected = false; } @@ -359,7 +359,7 @@ namespace Barotrauma //if clicking a selected entity, start moving it foreach (MapEntity e in selectedList) { - if (e.Contains(position)) startMovingPos = position; + if (e.IsMouseOn(position)) startMovingPos = position; } selectionPos = position; @@ -415,6 +415,16 @@ namespace Barotrauma } else { + if (editingHUD == null) return; + + foreach (GUIComponent component in editingHUD.children) + { + var textBox = component as GUITextBox; + if (textBox == null) continue; + + textBox.Deselect(); + } + editingHUD = null; } } diff --git a/Subsurface/Source/Map/MapEntityPrefab.cs b/Subsurface/Source/Map/MapEntityPrefab.cs index eb7aa06ea..8c0f35697 100644 --- a/Subsurface/Source/Map/MapEntityPrefab.cs +++ b/Subsurface/Source/Map/MapEntityPrefab.cs @@ -117,8 +117,13 @@ namespace Barotrauma if (placePosition == Vector2.Zero) { - if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) - placePosition = Submarine.MouseToWorldGrid(cam); + Vector2 position = Submarine.MouseToWorldGrid(cam); + + GUI.DrawLine(spriteBatch, new Vector2(position.X-GameMain.GraphicsWidth, -position.Y), new Vector2(position.X+GameMain.GraphicsWidth, -position.Y), Color.White); + + GUI.DrawLine(spriteBatch, new Vector2(position.X, position.Y - GameMain.GraphicsHeight), new Vector2(position.X, position.Y+GameMain.GraphicsHeight), Color.White); + + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) placePosition = position; } else { diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 903885616..ec56edaf5 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -268,6 +268,27 @@ namespace Barotrauma InsertToList(); } + + public override bool IsMouseOn(Vector2 position) + { + if (StairDirection == Direction.None) + { + return base.IsMouseOn(position); + } + else + { + if (!base.IsMouseOn(position)) return false; + + if (StairDirection == Direction.Left) + { + return MathUtils.LineToPointDistance(new Vector2(rect.X, rect.Y), new Vector2(rect.Right, rect.Y - rect.Height), position)< 40.0f; + } + else + { + return MathUtils.LineToPointDistance(new Vector2(rect.X,rect.Y-rect.Height), new Vector2(rect.Right, rect.Y), position) <40.0f; + } + } + } public override void Remove() { diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index 3ebce3b5e..0408d8360 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -16,7 +16,7 @@ namespace Barotrauma { public static List WayPointList = new List(); - public static bool ShowWayPoints, ShowSpawnPoints; + public static bool ShowWayPoints = true, ShowSpawnPoints = true; private SpawnType spawnType; @@ -75,6 +75,10 @@ namespace Barotrauma ConnectedGap = gap; } + public WayPoint(Rectangle rectangle) + : this (rectangle, Submarine.Loaded) + { } + public WayPoint(Rectangle newRect, Submarine submarine) : base (submarine) { @@ -88,18 +92,18 @@ namespace Barotrauma currentHull = Hull.FindHull(WorldPosition); } + public override bool IsMouseOn(Vector2 position) + { + if (IsHidden()) return false; + + return base.IsMouseOn(position); + } + public override void Draw(SpriteBatch spriteBatch, bool editing, bool back=true) { if (!editing && !GameMain.DebugDraw) return; - if (spawnType == SpawnType.Path) - { - if (!GameMain.DebugDraw && !ShowWayPoints) return; - } - else - { - if (!GameMain.DebugDraw && !ShowSpawnPoints) return; - } + if (IsHidden()) return; Rectangle drawRect = Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); @@ -119,6 +123,18 @@ namespace Barotrauma } } + private bool IsHidden() + { + if (spawnType == SpawnType.Path) + { + return (!GameMain.DebugDraw && !ShowWayPoints); + } + else + { + return (!GameMain.DebugDraw && !ShowSpawnPoints); + } + } + public override void DrawEditing(SpriteBatch spriteBatch, Camera cam) { if (editingHUD == null || editingHUD.UserData != this) @@ -247,6 +263,8 @@ namespace Barotrauma foreach (Hull hull in Hull.hullList) { + if (hull.Rect.Height < 150) continue; + WayPoint prevWaypoint = null; if (hull.Rect.Width { WayPoint.ShowWayPoints = !WayPoint.ShowWayPoints; return true; }; - tickBox = new GUITickBox(new Rectangle(0, y + 40, 20, 20), "Spawnpoints", Alignment.TopLeft, GUIpanel); + tickBox.Selected = true; + tickBox = new GUITickBox(new Rectangle(0, y + 45, 20, 20), "Spawnpoints", Alignment.TopLeft, GUIpanel); tickBox.OnSelected = (GUITickBox obj) => { WayPoint.ShowSpawnPoints = !WayPoint.ShowSpawnPoints; return true; }; - + tickBox.Selected = true; + tickBox = new GUITickBox(new Rectangle(0, y + 70, 20, 20), "Links", Alignment.TopLeft, GUIpanel); + tickBox.OnSelected = (GUITickBox obj) => { Item.ShowLinks = !Item.ShowLinks; return true; }; + tickBox.Selected = true; + tickBox = new GUITickBox(new Rectangle(0, y + 95, 20, 20), "Hulls", Alignment.TopLeft, GUIpanel); + tickBox.OnSelected = (GUITickBox obj) => { Hull.ShowHulls = !Hull.ShowHulls; return true; }; + tickBox.Selected = true; + tickBox = new GUITickBox(new Rectangle(0, y + 120, 20, 20), "Gaps", Alignment.TopLeft, GUIpanel); + tickBox.OnSelected = (GUITickBox obj) => { Gap.ShowGaps = !Gap.ShowGaps; return true; }; + tickBox.Selected = true; + } public void StartTutorial() @@ -186,9 +199,14 @@ namespace Barotrauma if (Submarine.Loaded != null) { cam.Position = Submarine.Loaded.Position + Submarine.HiddenSubPosition; - nameBox.Text = Submarine.Loaded.Name; + //nameBox.Text = Submarine.Loaded.Name; } - //CreateDummyCharacter(); + else + { + cam.Position = Submarine.HiddenSubPosition; + } + + cam.UpdateTransform(); } public override void Deselect() @@ -354,7 +372,7 @@ namespace Barotrauma MapEntity.UpdateSelecting(cam); } - + GUIComponent.MouseOn = null; GUIpanel.Update((float)deltaTime); if (selectedTab > -1) { diff --git a/Subsurface/Source/Utils/MathUtils.cs b/Subsurface/Source/Utils/MathUtils.cs index edf3d6a6a..781454bcf 100644 --- a/Subsurface/Source/Utils/MathUtils.cs +++ b/Subsurface/Source/Utils/MathUtils.cs @@ -188,6 +188,12 @@ namespace Barotrauma return (r >= 0 && r <= 1) && (s >= 0 && s <= 1); } + public static float LineToPointDistance(Vector2 lineA, Vector2 lineB, Vector2 point) + { + return (float)(Math.Abs((lineB.X-lineA.X)*(lineA.Y - point.Y) - (lineA.X - point.X)*(lineB.Y-lineA.Y)) / + Math.Sqrt(Math.Pow(lineB.X - lineA.X, 2) + Math.Pow(lineB.Y - lineA.Y, 2))); + } + public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect) { Vector2 circleDistance = new Vector2(Math.Abs(circlePos.X - rect.Center.X), Math.Abs(circlePos.Y -rect.Center.Y)); diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index cc99df16dab3ba9d67f95675d3805a15fb129d65..9e3968bcbd4f0c3620d7fb84c2fe9da322d348ed 100644 GIT binary patch delta 10871 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