using Barotrauma.Items.Components; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; 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 Submarine sub; private XElement saveElement; public override bool IsLinkable { get { return true; } } public LinkedSubmarine(Submarine submarine) : base(null, submarine) { linkedTo = new System.Collections.ObjectModel.ObservableCollection(); linkedToID = new List(); 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); foreach (MapEntity e in linkedTo) { GUI.DrawLine(spriteBatch, new Vector2(WorldPosition.X, -WorldPosition.Y), new Vector2(e.WorldPosition.X, -e.WorldPosition.Y), Color.Red * 0.3f); } } public override void DrawEditing(SpriteBatch spriteBatch, Camera cam) { if (editingHUD == null || editingHUD.UserData as LinkedSubmarine != this) { editingHUD = CreateEditingHUD(); } editingHUD.Draw(spriteBatch); editingHUD.Update((float)Physics.step); if (!PlayerInput.LeftButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return; Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); foreach (MapEntity entity in mapEntityList) { if (entity == this || !entity.IsHighlighted || !(entity is Item) || !entity.IsMouseOn(position)) continue; if (((Item)entity).GetComponent() == null) continue; if (linkedTo.Contains(entity)) { linkedTo.Remove(entity); } else { linkedTo.Add(entity); } } } private GUIComponent CreateEditingHUD(bool inGame = false) { int width = 450; int x = GameMain.GraphicsWidth / 2 - width / 2, y = 10; editingHUD = new GUIFrame(new Rectangle(x, y, width, 100), GUI.Style); editingHUD.Padding = new Vector4(10, 10, 0, 0); editingHUD.UserData = this; new GUITextBlock(new Rectangle(0, 0, 100, 20), "Linked submarine", GUI.Style, Alignment.TopLeft, Alignment.TopLeft, editingHUD, false, GUI.LargeFont); y += 20; if (!inGame) { new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to a docking port", GUI.Style, Alignment.TopRight, Alignment.TopRight, editingHUD).Font = GUI.SmallFont; y += 25; } return editingHUD; } 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) { XElement saveElement = null; if (sub == null) { var doc = Submarine.OpenFile(filePath); saveElement = doc.Root; saveElement.Name = "LinkedSubmarine"; saveElement.Add(new XAttribute("filepath", filePath)); var linkedPort = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent() != null); if (linkedPort != null) { saveElement.Add(new XAttribute("linkedto", linkedPort.ID)); } } else { if (!sub.DockedTo.Contains(Submarine.MainSub)) return null; saveElement = new XElement("LinkedSubmarine"); sub.SaveToXElement(saveElement); } saveElement.Add(new XAttribute("pos", ToolBox.Vector2ToString(Position - Submarine.HiddenSubPosition))); parentElement.Add(saveElement); return saveElement; } public static void Load(XElement element, Submarine submarine) { Vector2 pos = ToolBox.GetAttributeVector2(element, "pos", Vector2.Zero); LinkedSubmarine linkedSub = null; if (Screen.Selected == GameMain.EditMapScreen) { string filePath = ToolBox.GetAttributeString(element, "filepath", ""); linkedSub = Create(submarine, filePath, pos); } else { linkedSub = new LinkedSubmarine(submarine); linkedSub.saveElement = element; linkedSub.rect.Location = pos.ToPoint(); } string linkedToString = ToolBox.GetAttributeString(element, "linkedto", ""); if (linkedToString != "") { string[] linkedToIds = linkedToString.Split(','); for (int i = 0; i < linkedToIds.Length; i++) { linkedSub.linkedToID.Add((ushort)int.Parse(linkedToIds[i])); } } } public override void OnMapLoaded() { if (saveElement == null) return; sub = Submarine.Load(saveElement, false); sub.SetPosition(WorldPosition - Submarine.WorldPosition); sub.Submarine = Submarine; var linkedItem = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent() != null); if (linkedItem != null) { var linkedPort = ((Item)linkedItem).GetComponent(); DockingPort myPort = null; float closestDistance = 0.0f; foreach (DockingPort port in DockingPort.list) { if (port.Item.Submarine != sub || port.IsHorizontal != linkedPort.IsHorizontal) continue; float dist = Vector2.Distance(port.Item.WorldPosition, linkedPort.Item.WorldPosition); if (myPort == null || dist < closestDistance) { myPort = port; closestDistance = dist; } } if (myPort != null) { myPort.Dock(linkedPort); } } } } }