diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 23966ab7d..75e2e93c9 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -118,6 +118,7 @@ + @@ -143,6 +144,7 @@ + @@ -492,6 +494,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Subsurface/Content/Items/Door/dockingport.png b/Subsurface/Content/Items/Door/dockingport.png new file mode 100644 index 000000000..86d928cda Binary files /dev/null and b/Subsurface/Content/Items/Door/dockingport.png differ diff --git a/Subsurface/Content/Items/Door/doors.xml b/Subsurface/Content/Items/Door/doors.xml index e628549ff..7834d3d90 100644 --- a/Subsurface/Content/Items/Door/doors.xml +++ b/Subsurface/Content/Items/Door/doors.xml @@ -82,4 +82,29 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs index d8107f9d5..872c6b697 100644 --- a/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs +++ b/Subsurface/Source/Characters/AI/IndoorsSteeringManager.cs @@ -103,7 +103,7 @@ namespace Barotrauma private Vector2 DiffToCurrentNode() { - if (currentPath == null) return Vector2.Zero; + if (currentPath == null || currentPath.Finished) return Vector2.Zero; if (currentPath.Finished) { diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index b84831684..e25b591b2 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -616,20 +616,7 @@ namespace Barotrauma while (ce != null && ce.Contact != null) { ce.Contact.Enabled = false; - //if (ce.Contact.IsTouching && ce.Contact.Enabled && - // ((inToOut && ce.Contact.FixtureA.Body.UserData is Structure) || (!inToOut && ce.Contact.FixtureA.Body.UserData is Submarine))) - //{ - // Vector2 normal; - // FarseerPhysics.Common.FixedArray2 worldPoints; - // ce.Contact.GetWorldManifold(out normal, out worldPoints); - // foreach (Limb limb2 in Limbs) - // { - // limb2.body.FarseerBody.ApplyLinearImpulse(limb2.Mass * normal); - // } - - // return false; - //} ce = ce.Next; } } @@ -639,7 +626,7 @@ namespace Barotrauma limb.body.LinearVelocity += velocityChange; } - character.Stun = 0.1f; + //character.Stun = 0.1f; character.DisableImpactDamageTimer = 0.25f; SetPosition(refLimb.SimPosition + moveAmount); diff --git a/Subsurface/Source/GUI/GUIDropDown.cs b/Subsurface/Source/GUI/GUIDropDown.cs index 01fd79766..a6bd02787 100644 --- a/Subsurface/Source/GUI/GUIDropDown.cs +++ b/Subsurface/Source/GUI/GUIDropDown.cs @@ -34,7 +34,7 @@ namespace Barotrauma if (parent != null) parent.AddChild(this); - button = new GUIButton(Rectangle.Empty, "", Color.White, Alignment.TopLeft, Alignment.TopLeft, null, this); + button = new GUIButton(Rectangle.Empty, text, Color.White, Alignment.TopLeft, Alignment.TopLeft, null, this); button.TextColor = Color.White; button.Color = Color.Black * 0.8f; diff --git a/Subsurface/Source/Items/Components/DockingPort.cs b/Subsurface/Source/Items/Components/DockingPort.cs new file mode 100644 index 000000000..69347b7bc --- /dev/null +++ b/Subsurface/Source/Items/Components/DockingPort.cs @@ -0,0 +1,367 @@ +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Joints; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Barotrauma.Items.Components +{ + + class DockingPort : ItemComponent, IDrawableComponent + { + private static List list = new List(); + + private Sprite overlaySprite; + + private Vector2 distanceTolerance; + + private DockingPort dockingTarget; + + private float dockingState; + + private Joint joint; + + private int dockingDir; + + private Hull[] hulls; + + private Body[] bodies; + + private Gap gap; + + [HasDefaultValue("32.0,32.0", false)] + public string DistanceTolerance + { + get { return ToolBox.Vector2ToString(distanceTolerance); } + set { distanceTolerance = ToolBox.ParseToVector2(value); } + } + + [HasDefaultValue(32.0f, false)] + public float DockedDistance + { + get; + set; + } + + [HasDefaultValue(true, false)] + public bool IsHorizontal + { + get; + set; + } + + public override bool IsActive + { + get + { + return base.IsActive; + } + set + { + if (!IsActive && value) + { + if (dockingTarget == null) AttemptDock(); + if (dockingTarget == null) return; + + base.IsActive = value; + } + else if (IsActive && !value) + { + Undock(); + } + + //base.IsActive = value; + } + } + + public DockingPort(Item item, XElement element) + : base(item, element) + { + // isOpen = false; + foreach (XElement subElement in element.Elements()) + { + string texturePath = ToolBox.GetAttributeString(subElement, "texture", ""); + switch (subElement.Name.ToString().ToLowerInvariant()) + { + case "sprite": + overlaySprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.ConfigFile)); + break; + } + } + + list.Add(this); + } + + private void AttemptDock() + { + foreach (DockingPort port in list) + { + if (port == this || port.item.Submarine == item.Submarine) continue; + + if (Math.Abs(port.item.WorldPosition.X - item.WorldPosition.X) > distanceTolerance.X) continue; + if (Math.Abs(port.item.WorldPosition.Y - item.WorldPosition.Y) > distanceTolerance.Y) continue; + + Dock(port); + return; + + } + } + + private void Dock(DockingPort target) + { + if (dockingTarget!=null) + { + Undock(); + } + + dockingTarget = target; + dockingTarget.dockingTarget = this; + dockingTarget.IsActive = true; + + dockingDir = Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X); + dockingTarget.dockingDir = -dockingDir; + + CreateJoint(false); + } + + + private void CreateJoint(bool useWeldJoint) + { + Vector2 offset = (IsHorizontal ? + Vector2.UnitX * Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) : + Vector2.UnitY * Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y)); + offset *= DockedDistance * 0.5f; + + Vector2 pos1 = item.WorldPosition + offset; + + Vector2 pos2 = dockingTarget.item.WorldPosition - offset; + + if (useWeldJoint) + { + joint = JointFactory.CreateWeldJoint(GameMain.World, + item.Submarine.SubBody.Body, dockingTarget.item.Submarine.SubBody.Body, + ConvertUnits.ToSimUnits(pos1), FarseerPhysics.ConvertUnits.ToSimUnits(pos2), true); + + ((WeldJoint)joint).FrequencyHz = 1.0f; + } + else + { + var distanceJoint = JointFactory.CreateDistanceJoint(GameMain.World, + item.Submarine.SubBody.Body, dockingTarget.item.Submarine.SubBody.Body, + ConvertUnits.ToSimUnits(pos1), FarseerPhysics.ConvertUnits.ToSimUnits(pos2), true); + + distanceJoint.Length = 0.01f; + distanceJoint.Frequency = 1.0f; + distanceJoint.DampingRatio = 0.8f; + + joint = distanceJoint; + } + + + joint.CollideConnected = true; + } + + private void CreateHull() + { + var hullRects = new Rectangle[] { item.WorldRect, dockingTarget.item.WorldRect }; + var subs = new Submarine[] { item.Submarine, dockingTarget.item.Submarine }; + + hulls = new Hull[2]; + bodies = new Body[4]; + if (IsHorizontal) + { + if (hullRects[0].Center.X > hullRects[1].Center.X) + { + hullRects = new Rectangle[] { dockingTarget.item.WorldRect, item.WorldRect }; + subs = new Submarine[] { dockingTarget.item.Submarine,item.Submarine }; + } + + + hullRects[0] = new Rectangle(hullRects[0].Center.X, hullRects[0].Y, ((int)DockedDistance / 2), hullRects[0].Height); + hullRects[1] = new Rectangle(hullRects[1].Center.X - ((int)DockedDistance / 2), hullRects[1].Y, ((int)DockedDistance / 2), hullRects[1].Height); + + + + for (int i = 0; i < 2;i++ ) + { + hullRects[i].Location -= (subs[i].WorldPosition - subs[i].HiddenSubPosition).ToPoint(); + hulls[i] = new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), hullRects[i], subs[i]); + hulls[i].AddToGrid(subs[i]); + + + for (int j = 0; j < 2; j++) + { + bodies[i + j * 2] = BodyFactory.CreateEdge(GameMain.World, + ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X, hullRects[i].Y - hullRects[i].Height * j)), + ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j))); + + //bodies[i + j * 2] = BodyFactory.CreateRectangle(GameMain.World, ConvertUnits.ToSimUnits(hullRects[i].Width), 0.1f, 5.0f); + //bodies[i + j * 2].SetTransform(ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Center.X, hullRects[i].Y - (hullRects[i].Height+5) * j)), 0.0f); + } + } + + gap = new Gap(new Rectangle(hullRects[0].Right-2, hullRects[0].Y, 4, hullRects[0].Height), true, item.Submarine); + gap.linkedTo.Clear(); + gap.linkedTo.Add(hulls[0]); + gap.linkedTo.Add(hulls[1]); + + //var hullRect1 = new Rectangle(hullRects.Min(h => h.Center.X), hullRect.Y, ((int)DockedDistance / 2), hullRects[0].Height); + //var hullRect2 = new Rectangle(hullRects.Max(h => h.Center.X), hullRect.Y, ((int)DockedDistance / 2), hullRects[0].Height); + + //var sub1 = hullRect.Center.X < targetRect.Center.X ? item.Submarine : dockingTarget.item.Submarine; + //var sub2 = hullRect.Center.X > targetRect.Center.X ? item.Submarine : dockingTarget.item.Submarine; + + // hullRect1.Location -= (sub1.WorldPosition - sub1.HiddenSubPosition).ToPoint(); + //hulls[0] = new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), hullRect1, sub1); + //hulls[0].AddToGrid(sub1); + + //hullRect2.Location -= (sub2.WorldPosition - sub2.HiddenSubPosition).ToPoint(); + //hulls[1] = new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), hullRect2, sub2); + //hulls[1].AddToGrid(sub2); + + + } + else + { + //hullRect = new Rectangle(hullRect.X, + // Math.Max(hullRect.Y - hullRect.Height / 2, targetRect.Y - targetRect.Height / 2), hullRect.Width, (int)DockedDistance); + } + + foreach (Body body in bodies) + { + body.BodyType = BodyType.Static; + body.Friction = 0.5f; + + body.CollisionCategories = Physics.CollisionWall; + } + + + } + + private void Undock() + { + if (dockingTarget == null) return; + + dockingTarget.dockingTarget = null; + dockingTarget.IsActive = false; + + dockingTarget = null; + + GameMain.World.RemoveJoint(joint); + joint = null; + + hulls[0].Remove(); + hulls[1].Remove(); + + gap.Remove(); + gap = null; + + foreach (Body body in bodies) + { + GameMain.World.RemoveBody(body); + } + bodies = null; + + + + //foreach (Gap g in hulls[0].ConnectedGaps) + //{ + // g.Remove(); + //} + + //foreach (Gap g in hulls[1].ConnectedGaps) + //{ + // g.Remove(); + //} + + + hulls = null; + } + + public override void Update(float deltaTime, Camera cam) + { + if (dockingTarget==null) + { + dockingState = MathHelper.Lerp(dockingState, 0.0f, deltaTime * 10.0f); + if (dockingState < 0.01f) base.IsActive = false; + } + else + { + if (joint is DistanceJoint && Vector2.Distance(joint.WorldAnchorA, joint.WorldAnchorB) < 0.05f) + { + GameMain.World.RemoveJoint(joint); + + CreateJoint(true); + CreateHull(); + } + + dockingState = MathHelper.Lerp(dockingState, 1.0f, deltaTime * 10.0f); + } + } + + public void Draw(SpriteBatch spriteBatch, bool editing) + { + if (dockingState == 0.0f) return; + + Vector2 drawPos = item.DrawPosition; + drawPos.Y = -drawPos.Y; + + var rect = overlaySprite.SourceRect; + drawPos.Y -= rect.Height / 2; + + if (IsHorizontal) + { + if (dockingDir == 1) + { + spriteBatch.Draw(overlaySprite.Texture, + drawPos, + new Rectangle( + rect.Center.X + (int)(rect.Width / 2 * (1.0f - dockingState)), rect.Y, + (int)(rect.Width / 2 * dockingState), rect.Height), Color.White); + + } + else + { + spriteBatch.Draw(overlaySprite.Texture, + drawPos - Vector2.UnitX * (rect.Width / 2 * dockingState), + new Rectangle( + rect.X, rect.Y, + (int)(rect.Width / 2 * dockingState), rect.Height), Color.Red); + } + } + else + { + if (dockingDir == 1) + { + spriteBatch.Draw(overlaySprite.Texture, + drawPos, + new Rectangle( + rect.X, rect.Y + rect.Height/2 + (int)(rect.Height / 2 * (1.0f - dockingState)), + rect.Width, (int)(rect.Height / 2 * dockingState)), Color.White); + + } + else + { + spriteBatch.Draw(overlaySprite.Texture, + drawPos - Vector2.UnitY * (rect.Height / 2 * dockingState), + new Rectangle( + rect.X, rect.Y, + rect.Width, (int)(rect.Height / 2 * dockingState)), Color.Red); + } + } + } + + protected override void RemoveComponentSpecific() + { + list.Remove(this); + } + } +} diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 2243aef2e..836cbec2f 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1432,7 +1432,7 @@ namespace Barotrauma return true; } - public override XElement Save(XDocument doc) + public override XElement Save(XElement parentElement) { XElement element = new XElement("Item"); @@ -1475,7 +1475,7 @@ namespace Barotrauma ic.Save(element); } - doc.Root.Add(element); + parentElement.Add(element); return element; } diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 31452a2ca..621b15049 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -639,7 +639,7 @@ namespace Barotrauma FindHulls(); } - public override XElement Save(XDocument doc) + public override XElement Save(XElement parentElement) { XElement element = new XElement("Gap"); @@ -663,7 +663,7 @@ namespace Barotrauma // } //} - doc.Root.Add(element); + parentElement.Add(element); return element; } diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index d124b5305..f0f2c8793 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -260,6 +260,21 @@ namespace Barotrauma } } + public void AddToGrid(Submarine submarine) + { + foreach (EntityGrid grid in entityGrids) + { + if (grid.Submarine != submarine) continue; + + rect.Location -= submarine.HiddenSubPosition.ToPoint(); + + grid.InsertEntity(this); + + rect.Location += submarine.HiddenSubPosition.ToPoint(); + return; + } + } + public override bool IsMouseOn(Vector2 position) { if (!GameMain.DebugDraw && !ShowHulls) return false; @@ -295,6 +310,7 @@ namespace Barotrauma public override void Remove() { base.Remove(); + hullList.Remove(this); if (Submarine == null || !Submarine.Loading) { @@ -324,7 +340,6 @@ namespace Barotrauma } - hullList.Remove(this); } public void AddFireSource(FireSource fireSource, bool createNetworkEvent = true) @@ -712,7 +727,7 @@ namespace Barotrauma // return gaps; //} - public override XElement Save(XDocument doc) + public override XElement Save(XElement parentElement) { XElement element = new XElement("Hull"); @@ -725,8 +740,8 @@ namespace Barotrauma rect.Width + "," + rect.Height), new XAttribute("water", volume) ); - - doc.Root.Add(element); + + parentElement.Add(element); return element; } diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index ed9051a21..958655ce0 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -596,7 +596,7 @@ namespace Barotrauma } - public virtual XElement Save(XDocument doc) + public virtual XElement Save(XElement parentElement) { DebugConsole.ThrowError("Saving entity " + GetType() + " failed."); return null; @@ -625,15 +625,29 @@ namespace Barotrauma if (linked != null) e.linkedTo.Add(linked); } } + + List linkedSubs = new List(); for (int i = 0; i false + + Submarine sub = new Submarine(ToolBox.GetAttributeString(element, "name", "")); + sub.Load(unloadPrevious, element); + + return sub; } public static Submarine Load(string fileName, bool unloadPrevious) @@ -851,7 +880,7 @@ namespace Barotrauma string path = string.IsNullOrWhiteSpace(folder) ? fileName : System.IO.Path.Combine(SavePath, fileName); Submarine sub = new Submarine(path); - sub.Load(false); + sub.Load(unloadPrevious); //Entity.dictionary.Add(int.MaxValue, sub); diff --git a/Subsurface/Source/Map/SubmarineBody.cs b/Subsurface/Source/Map/SubmarineBody.cs index 1cc21f166..2802e4fd0 100644 --- a/Subsurface/Source/Map/SubmarineBody.cs +++ b/Subsurface/Source/Map/SubmarineBody.cs @@ -125,30 +125,16 @@ namespace Barotrauma Body, this); } - //foreach (Hull hull in Hull.hullList) - //{ - // Rectangle rect = hull.Rect; - // foreach (Structure wall in Structure.WallList) - // { - // if (!Submarine.RectsOverlap(wall.Rect, hull.Rect)) continue; - - // Rectangle wallRect = wall.IsHorizontal ? - // new Rectangle(hull.Rect.X, wall.Rect.Y, hull.Rect.Width, wall.Rect.Height) : - // new Rectangle(wall.Rect.X, hull.Rect.Y, wall.Rect.Width, hull.Rect.Height); - - // rect = Rectangle.Union( - // new Rectangle(wallRect.X, wallRect.Y - wallRect.Height, wallRect.Width, wallRect.Height), - // new Rectangle(rect.X, rect.Y - rect.Height, rect.Width, rect.Height)); - // rect.Y = rect.Y + rect.Height; - // } - - // FixtureFactory.AttachRectangle( - // ConvertUnits.ToSimUnits(rect.Width), - // ConvertUnits.ToSimUnits(rect.Height), - // 5.0f, - // ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2)), - // body, this); - //} + foreach (Hull hull in Hull.hullList) + { + Rectangle rect = hull.Rect; + FixtureFactory.AttachRectangle( + ConvertUnits.ToSimUnits(rect.Width), + ConvertUnits.ToSimUnits(rect.Height), + 5.0f, + ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2)), + Body, this); + } } @@ -183,50 +169,17 @@ namespace Barotrauma List points = new List(); - Vector2 leftMost = Vector2.Zero; - foreach (Structure wall in Structure.WallList) { if (wall.Submarine != submarine) continue; - for (int x = -1; x <= 1; x += 2) - { - for (int y = -1; y <= 1; y += 2) - { - Vector2 corner = new Vector2(wall.Rect.X + wall.Rect.Width / 2.0f, wall.Rect.Y - wall.Rect.Height / 2.0f); - corner.X += x * wall.Rect.Width / 2.0f; - corner.Y += y * wall.Rect.Height / 2.0f; - - if (points.Contains(corner)) continue; - - points.Add(corner); - if (leftMost == Vector2.Zero || corner.X < leftMost.X) leftMost = corner; - } - } + points.Add(new Vector2(wall.Rect.X, wall.Rect.Y)); + points.Add(new Vector2(wall.Rect.X + wall.Rect.Width, wall.Rect.Y)); + points.Add(new Vector2(wall.Rect.X, wall.Rect.Y - wall.Rect.Height)); + points.Add(new Vector2(wall.Rect.X + wall.Rect.Width, wall.Rect.Y - wall.Rect.Height)); } - List hullPoints = new List(); - - Vector2 currPoint = leftMost; - Vector2 endPoint; - do - { - hullPoints.Add(currPoint); - endPoint = points[0]; - - for (int i = 1; i < points.Count; i++) - { - if ((currPoint == endPoint) - || (MathUtils.VectorOrientation(currPoint, endPoint, points[i]) == -1)) - { - endPoint = points[i]; - } - } - - currPoint = endPoint; - - } - while (endPoint != hullPoints[0]); + List hullPoints = MathUtils.GiftWrap(points); return hullPoints; } diff --git a/Subsurface/Source/Map/SubmarineLink.cs b/Subsurface/Source/Map/SubmarineLink.cs new file mode 100644 index 000000000..68f671656 --- /dev/null +++ b/Subsurface/Source/Map/SubmarineLink.cs @@ -0,0 +1,171 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Barotrauma +{ + + class LinkedSubmarinePrefab : MapEntityPrefab + { + public readonly Submarine mainSub; + + public LinkedSubmarinePrefab(Submarine submarine) + { + this.mainSub = submarine; + } + + protected override void CreateInstance(Rectangle rect) + { + System.Diagnostics.Debug.Assert(Submarine.MainSub != null); + + LinkedSubmarine.Create(Submarine.MainSub, mainSub.FilePath, rect.Location.ToVector2()); + } + } + + class LinkedSubmarine : MapEntity + { + private List wallVertices; + + private string filePath; + + private XElement saveElement; + + public LinkedSubmarine(Submarine submarine) + : base(null, submarine) + { + InsertToList(); + } + + public static LinkedSubmarine Create(Submarine mainSub, string filePath, Vector2 position) + { + LinkedSubmarine sl = new LinkedSubmarine(mainSub); + sl.filePath = filePath; + + XDocument doc = Submarine.OpenFile(filePath); + if (doc == null || doc.Root == null) return null; + + sl.GenerateWallVertices(doc.Root); + + //for (int i = 0; i < sl.wallVertices.Count; i++) + //{ + // sl.wallVertices[i] = sl.wallVertices[i] += position; + //} + + sl.Rect = new Rectangle( + (int)sl.wallVertices.Min(v => v.X + position.X), + (int)sl.wallVertices.Max(v => v.Y + position.Y), + (int)sl.wallVertices.Max(v => v.X + position.X), + (int)sl.wallVertices.Min(v => v.Y + position.Y)); + + sl.rect = new Rectangle(sl.rect.X, sl.rect.Y, sl.rect.Width - sl.rect.X, sl.rect.Y - sl.rect.Height); + + return sl; + } + + public override bool IsMouseOn(Vector2 position) + { + return Vector2.Distance(position, WorldPosition) < 50.0f; + } + + public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) + { + if (!editing || wallVertices == null) return; + + Color color = (isHighlighted) ? Color.Orange : Color.Green; + if (isSelected) color = Color.Red; + + Vector2 pos = new Vector2(rect.X + rect.Width/2, rect.Y - rect.Height/2); + + for (int i = 0; i < wallVertices.Count; i++) + { + Vector2 startPos = wallVertices[i] + pos; + startPos.Y = -startPos.Y; + + Vector2 endPos = wallVertices[(i + 1) % wallVertices.Count] + pos; + endPos.Y = -endPos.Y; + + GUI.DrawLine(spriteBatch, + startPos, + endPos, + color, 0.0f, 5); + } + + pos.Y = -pos.Y; + GUI.DrawLine(spriteBatch, pos + Vector2.UnitY * 50.0f, pos - Vector2.UnitY * 50.0f, color, 0.0f, 5); + GUI.DrawLine(spriteBatch, pos + Vector2.UnitX * 50.0f, pos - Vector2.UnitX * 50.0f, color, 0.0f, 5); + + } + + private void GenerateWallVertices(XElement rootElement) + { + List points = new List(); + + var wallPrefabs = + MapEntityPrefab.list.FindAll(mp => (mp is StructurePrefab) && ((StructurePrefab)mp).HasBody); + + foreach (XElement element in rootElement.Elements()) + { + if (element.Name != "Structure") continue; + + string name = ToolBox.GetAttributeString(element, "name", ""); + if (!wallPrefabs.Any(wp => wp.Name == name)) continue; + + var rect = ToolBox.GetAttributeVector4(element, "rect", Vector4.Zero); + + points.Add(new Vector2(rect.X, rect.Y)); + points.Add(new Vector2(rect.X + rect.Z, rect.Y)); + points.Add(new Vector2(rect.X, rect.Y - rect.W)); + points.Add(new Vector2(rect.X + rect.Z, rect.Y - rect.W)); + } + + wallVertices = MathUtils.GiftWrap(points); + } + + public override XElement Save(XElement parentElement) + { + var doc = Submarine.OpenFile(filePath); + + doc.Root.Name = "LinkedSubmarine"; + + doc.Root.Add( + new XAttribute("filepath", filePath), + new XAttribute("pos", ToolBox.Vector2ToString(Position - Submarine.HiddenSubPosition))); + + parentElement.Add(doc.Root); + + return doc.Root; + } + + public static void Load(XElement element, Submarine submarine) + { + Vector2 pos = ToolBox.GetAttributeVector2(element, "pos", Vector2.Zero); + + if (Screen.Selected == GameMain.EditMapScreen) + { + string filePath = ToolBox.GetAttributeString(element, "filepath", ""); + + Create(submarine, filePath, pos); + + return; + } + + var ls = new LinkedSubmarine(submarine); + ls.saveElement = element; + + ls.rect.Location = pos.ToPoint(); + } + + public override void OnMapLoaded() + { + if (saveElement == null) return; + var sub = Submarine.Load(saveElement, false); + sub.SetPosition(WorldPosition - Submarine.WorldPosition); + sub.Submarine = Submarine; + } + } +} diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index a5f9a49c4..1a0f77b70 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -718,7 +718,7 @@ namespace Barotrauma } } - public override XElement Save(XDocument doc) + public override XElement Save(XElement parentElement) { if (MoveWithLevel) return null; XElement element = new XElement("WayPoint"); @@ -739,7 +739,7 @@ namespace Barotrauma if (ConnectedGap != null) element.Add(new XAttribute("gap", ConnectedGap.ID)); if (Ladders != null) element.Add(new XAttribute("ladders", Ladders.Item.ID)); - doc.Root.Add(element); + parentElement.Add(element); if (linkedTo != null) { diff --git a/Subsurface/Source/Screens/EditMapScreen.cs b/Subsurface/Source/Screens/EditMapScreen.cs index fa3599308..3d84d432f 100644 --- a/Subsurface/Source/Screens/EditMapScreen.cs +++ b/Subsurface/Source/Screens/EditMapScreen.cs @@ -94,13 +94,23 @@ namespace Barotrauma button = new GUIButton(new Rectangle(310,0,70,20), "Save", GUI.Style, topPanel); button.OnClicked = SaveSub; - new GUITextBlock(new Rectangle(400, 0, 100, 20), "Description: ", GUI.Style, topPanel); + new GUITextBlock(new Rectangle(390, 0, 100, 20), "Description: ", GUI.Style, topPanel); - descriptionBox = new GUITextBox(new Rectangle(500, 0, 200, 20), null, null, Alignment.TopLeft, + descriptionBox = new GUITextBox(new Rectangle(490, 0, 200, 20), null, null, Alignment.TopLeft, Alignment.TopLeft, GUI.Style, topPanel); descriptionBox.Wrap = true; descriptionBox.OnSelected += ExpandDescriptionBox; descriptionBox.OnTextChanged = ChangeSubDescription; + + + //new GUITextBlock(new Rectangle(390, 0, 100, 20), "Link ", GUI.Style, topPanel); + + var linkedSubBox = new GUIDropDown(new Rectangle(750,0,200,20), "Add submarine", GUI.Style, topPanel); + foreach (Submarine sub in Submarine.SavedSubmarines) + { + linkedSubBox.AddItem(sub.Name, sub); + } + linkedSubBox.OnSelected += SelectLinkedSub; leftPanel = new GUIFrame(new Rectangle(0, 30, 150, GameMain.GraphicsHeight-30), GUI.Style); leftPanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); @@ -524,6 +534,18 @@ namespace Barotrauma return frame; } + private bool SelectLinkedSub(GUIComponent selected) + { + var submarine = selected.UserData as Submarine; + if (submarine == null) return false; + + var prefab = new LinkedSubmarinePrefab(submarine); + + MapEntityPrefab.SelectPrefab(prefab); + + return true; + } + private bool SelectWire(GUIComponent component, object userData) { if (dummyCharacter == null) return false; diff --git a/Subsurface/Source/Utils/MathUtils.cs b/Subsurface/Source/Utils/MathUtils.cs index 37fc9339f..cb0b98697 100644 --- a/Subsurface/Source/Utils/MathUtils.cs +++ b/Subsurface/Source/Utils/MathUtils.cs @@ -288,6 +288,41 @@ namespace Barotrauma return triangles; } + public static List GiftWrap(List points) + { + Vector2 leftMost = points[0]; + foreach (Vector2 point in points) + { + if (point.X < leftMost.X) leftMost = point; + } + + List wrappedPoints = new List(); + + Vector2 currPoint = leftMost; + Vector2 endPoint; + do + { + wrappedPoints.Add(currPoint); + endPoint = points[0]; + + for (int i = 1; i < points.Count; i++) + { + if (points[i] == currPoint) continue; + if (currPoint == endPoint || + MathUtils.VectorOrientation(currPoint, endPoint, points[i]) == -1) + { + endPoint = points[i]; + } + } + + currPoint = endPoint; + + } + while (endPoint != leftMost); + + return wrappedPoints; + } + public static List GenerateJaggedLine(Vector2 start, Vector2 end, int generations, float offsetAmount) { List segments = new List();