3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
24 public override ImmutableHashSet<Identifier>
Tags =>
null;
30 public override ImmutableHashSet<string>
Aliases {
get; }
48 private List<Vector2> wallVertices;
50 private string filePath;
56 private ushort originalMyPortID;
60 private ushort originalLinkedToID;
64 private bool purchasedLostShuttles;
74 private XElement saveElement;
76 private Vector2? positionRelativeToMainSub;
89 : base(null, submarine, id)
95 DebugConsole.Log(
"Created linked submarine (" +
ID +
")");
111 if (doc ==
null || doc.Root ==
null)
return null;
114 sl.filePath = filePath;
115 sl.saveElement = doc.Root;
116 sl.saveElement.Name =
"LinkedSubmarine";
117 sl.saveElement.SetAttributeValue(
"filepath", filePath);
125 sl.GenerateWallVertices(element);
126 sl.
CargoCapacity = element.GetAttributeInt(
"cargocapacity", 0);
127 if (sl.wallVertices.Any())
129 sl.
Rect =
new Rectangle(
130 (
int)sl.wallVertices.Min(v => v.X + position.X),
131 (
int)sl.wallVertices.Max(v => v.Y + position.Y),
132 (
int)sl.wallVertices.Max(v => v.X + position.X),
133 (
int)sl.wallVertices.Min(v => v.Y + position.Y));
135 int width = sl.
rect.Width - sl.
rect.X;
136 int height = sl.
rect.Y - sl.
rect.Height;
137 sl.
Rect =
new Rectangle((
int)(position.X - width / 2), (
int)(position.Y + height / 2), width, height);
141 sl.
Rect =
new Rectangle((
int)position.X, (
int)position.Y, 10, 10);
153 XElement cloneElement =
new XElement(saveElement);
155 sl.saveElement = cloneElement;
156 sl.filePath = filePath;
160 private void GenerateWallVertices(XElement rootElement)
162 List<Vector2> points =
new List<Vector2>();
164 foreach (XElement element
in rootElement.Elements())
166 if (element.Name !=
"Structure") {
continue; }
168 string name = element.GetAttributeString(
"name",
"");
169 Identifier identifier = element.GetAttributeIdentifier(
"identifier",
"");
171 StructurePrefab prefab = Structure.FindPrefab(name, identifier);
172 if (prefab ==
null) {
continue; }
174 float scale = element.GetAttributeFloat(
"scale", prefab.Scale);
176 var
rect = element.GetAttributeVector4(
"rect", Vector4.Zero);
177 if (!prefab.ResizeHorizontal) {
rect.Z *= scale / prefab.Scale; }
178 if (!prefab.ResizeVertical) {
rect.W *= scale / prefab.Scale; }
180 points.Add(
new Vector2(
rect.X,
rect.Y));
186 wallVertices = MathUtils.GiftWrap(points);
197 linkedSub =
CreateDummy(submarine, element, pos,
id);
198 linkedSub.saveElement =
new XElement(element);
199 linkedSub.purchasedLostShuttles =
false;
207 purchasedLostShuttles =
210 saveElement =
new XElement(element)
213 bool levelMatches =
string.IsNullOrWhiteSpace(levelSeed) || levelData ==
null || levelData.
Seed == levelSeed;
219 linkedSub.loadSub =
true;
220 linkedSub.
rect.Location = MathUtils.ToPoint(pos);
224 linkedSub.loadSub =
false;
228 #warning TODO: revise
231 for (
int i = 0; i < linkedToIds.Length; i++)
236 linkedSub.originalMyPortID = (ushort)element.
GetAttributeInt(
"originalmyport", 0);
239 return linkedSub.loadSub ? linkedSub :
null;
256 if (positionRelativeToMainSub.HasValue)
260 positionRelativeToMainSub =
null;
265 if (!loadSub) {
return; }
270 DebugConsole.ThrowError(
"Failed to load a linked submarine (empty XML element). The save file may be corrupted.");
273 if (!info.
SubmarineElement.Elements().Any(e => e.Name.ToString().Equals(
"hull", StringComparison.OrdinalIgnoreCase)))
275 DebugConsole.ThrowError(
"Failed to load a linked submarine (the submarine contains no hulls).");
279 saveElement.Attribute(
"purchasedlostshuttle")?.Remove();
291 Vector2 worldPos = saveElement.GetAttributeVector2(
"worldpos", Vector2.Zero);
292 if (worldPos != Vector2.Zero)
308 MapEntity linkedItem =
linkedTo.FirstOrDefault(lt => (lt as
Item)?.GetComponent<DockingPort>() !=
null);
309 if (linkedItem ==
null)
311 linkedPort =
DockingPort.
List.FirstOrDefault(dp => dp.DockingTarget !=
null && dp.DockingTarget.Item.Submarine == sub);
318 if (linkedPort ==
null)
320 if (purchasedLostShuttles)
322 linkedPort = (
FindEntityByID(originalLinkedToID) as
Item)?.GetComponent<DockingPort>();
326 if (linkedPort ==
null)
328 if (worldPos == Vector2.Zero)
330 Vector2 relativePos = saveElement.GetAttributeVector2(
"posrelativetomainsub", Vector2.Zero);
331 if (relativePos != Vector2.Zero)
333 positionRelativeToMainSub = relativePos;
337 DebugConsole.ThrowError(
"Something went wrong when loading a linked submarine - the save didn't include a world position, a linked port or position relative to the main sub.");
347 originalLinkedPort = linkedPort;
349 ushort originalMyId = childRemap.
GetOffsetId(originalMyPortID);
353 float closestDistance = 0.0f;
360 if (myPort ==
null || dist < closestDistance)
363 closestDistance = dist;
370 originalMyPortID = myPort.
Item.
ID;
372 myPort.
Undock(applyEffects:
false);
390 Vector2.UnitX * myPort.
GetDir(linkedPort) :
391 Vector2.UnitY * myPort.
GetDir(linkedPort);
396 myPort.
Dock(linkedPort);
397 myPort.
Lock(isNetworkMessage:
true, applyEffects:
false);
402 (campaign.PurchasedLostShuttles || campaign.PurchasedLostShuttlesInLatestSave))
409 wall.
SetDamage(i, 0, createNetworkEvent:
false, createExplosionEffect:
false);
425 public override XElement
Save(XElement parentElement)
427 XElement saveElement =
null;
431 if (this.saveElement ==
null)
434 saveElement = doc.Root;
435 saveElement.Add(
new XAttribute(
"filepath", filePath));
439 saveElement = this.saveElement;
441 saveElement.Name =
"LinkedSubmarine";
443 if (saveElement.Attribute(
"previewimage") !=
null)
445 saveElement.Attribute(
"previewimage").Remove();
450 saveElement.SetAttributeValue(
"purchasedlostshuttle",
true);
458 saveElement =
new XElement(
"LinkedSubmarine");
466 if (linkedPort !=
null)
468 saveElement.SetAttributeValue(
"linkedto", linkedPort.ID);
472 saveElement.SetAttributeValue(
"originallinkedto", originalLinkedPort !=
null ? originalLinkedPort.
Item.
ID : originalLinkedToID);
473 saveElement.SetAttributeValue(
"originalmyport", originalMyPortID);
477 bool leaveBehind =
false;
499 saveElement.SetAttributeValue(
"worldpos", XMLExtensions.Vector2ToString(position));
503 if (saveElement.Attribute(
"location") !=
null) { saveElement.Attribute(
"location").Remove(); }
504 if (saveElement.Attribute(
"worldpos") !=
null) { saveElement.Attribute(
"worldpos").Remove(); }
510 parentElement.Add(saveElement);
SubmarineInfo PendingSubmarineSwitch
virtual bool PurchasedLostShuttles
string? GetAttributeString(string key, string? def)
int?[] GetAttributeIntArray(string key, int[]? def)
Vector2 GetAttributeVector2(string key, in Vector2 def)
ContentPath? GetAttributeContentPath(string key)
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
virtual Vector2 WorldPosition
const ushort NullEntityID
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
static GameSession GameSession
static readonly Screen SubEditorScreen
BallastFloraBehavior BallastFlora
static readonly List< Hull > HullList
ushort GetOffsetId(XElement element)
void AssignMaxId(out ushort result)
static IEnumerable< DockingPort > List
int GetDir(DockingPort dockingTarget=null)
DockingPort DockingTarget
void Undock(bool applyEffects=true)
void Lock(bool isNetworkMessage, bool applyEffects=true)
DirectionType ForceDockingDirection
void Dock(DockingPort target)
override MapEntity Clone()
override XElement Save(XElement parentElement)
static LinkedSubmarine Load(ContentXElement element, Submarine submarine, IdRemap idRemap)
override void OnMapLoaded()
void SetPositionRelativeToMainSub()
static LinkedSubmarine CreateDummy(Submarine mainSub, string filePath, Vector2 position)
void LinkDummyToMainSubmarine()
LinkedSubmarine(Submarine submarine, ushort id=Entity.NullEntityID)
override bool IsMouseOn(Vector2 position)
ushort OriginalLinkedToID
static LinkedSubmarine CreateDummy(Submarine mainSub, Submarine linkedSub)
static LinkedSubmarine CreateDummy(Submarine mainSub, XElement element, Vector2 position, ushort id=Entity.NullEntityID)
override ImmutableHashSet< Identifier > Tags
readonly SubmarineInfo subInfo
override string OriginalName
override LocalizedString Name
override void CreateInstance(Rectangle rect)
LinkedSubmarinePrefab(SubmarineInfo subInfo)
override ImmutableHashSet< string > Aliases
override ImmutableHashSet< Identifier > AllowedLinks
override MapEntityCategory Category
MapEntity(MapEntityPrefab prefab, Submarine submarine, ushort id)
List< ushort > linkedToID
override Vector2 Position
readonly List< MapEntity > linkedTo
void SetDamage(int sectionIndex, float damage, Character attacker=null, bool createNetworkEvent=true, bool isNetworkEvent=true, bool createExplosionEffect=true, bool createWallDamageProjectiles=false)
static List< Structure > WallList
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
override Vector2? WorldPosition
void SaveToXElement(XElement element)
IEnumerable< Submarine > DockedTo
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
static Submarine Load(SubmarineInfo info, bool unloadPrevious, IdRemap linkedRemap=null)
Vector2 HiddenSubPosition
Rectangle GetDockedBorders(bool allowDifferentTeam=true)
Returns a rect that contains the borders of this sub and all subs docked to it, excluding outposts
void SetPosition(Vector2 position, List< Submarine > checkd=null, bool forceUndockFromStaticSubmarines=true)
XElement SubmarineElement
static XDocument OpenFile(string file)
SubmarineClass SubmarineClass