From b04e204dc39e9a025366328d296ed14e057e75bc Mon Sep 17 00:00:00 2001 From: Regalis Date: Sun, 26 Jun 2016 14:31:00 +0300 Subject: [PATCH] - multiple submarines can be "merged" into one file (to be used as escape vessels etc) - WIP docking ports --- Subsurface/Barotrauma.csproj | 5 + Subsurface/Content/Items/Door/dockingport.png | Bin 0 -> 28244 bytes Subsurface/Content/Items/Door/doors.xml | 25 ++ .../Characters/AI/IndoorsSteeringManager.cs | 2 +- .../Source/Characters/Animation/Ragdoll.cs | 15 +- Subsurface/Source/GUI/GUIDropDown.cs | 2 +- .../Source/Items/Components/DockingPort.cs | 367 ++++++++++++++++++ Subsurface/Source/Items/Item.cs | 4 +- Subsurface/Source/Map/Gap.cs | 4 +- Subsurface/Source/Map/Hull.cs | 23 +- Subsurface/Source/Map/MapEntity.cs | 18 +- Subsurface/Source/Map/MapEntityPrefab.cs | 15 +- Subsurface/Source/Map/Structure.cs | 4 +- Subsurface/Source/Map/Submarine.cs | 51 ++- Subsurface/Source/Map/SubmarineBody.cs | 77 +--- Subsurface/Source/Map/SubmarineLink.cs | 171 ++++++++ Subsurface/Source/Map/WayPoint.cs | 4 +- Subsurface/Source/Screens/EditMapScreen.cs | 26 +- Subsurface/Source/Utils/MathUtils.cs | 35 ++ 19 files changed, 740 insertions(+), 108 deletions(-) create mode 100644 Subsurface/Content/Items/Door/dockingport.png create mode 100644 Subsurface/Source/Items/Components/DockingPort.cs create mode 100644 Subsurface/Source/Map/SubmarineLink.cs 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 0000000000000000000000000000000000000000..86d928cdae6fa9fa02f91f6cc8c2ed488f0a82eb GIT binary patch literal 28244 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 AivR!s literal 0 HcmV?d00001 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();